🎨 Exploring the Abstract Factory Design Pattern in Ruby

🎨 Exploring the Abstract Factory Design Pattern in Ruby

Welcome! Today, let’s embark on a journey into the Abstract Factory design pattern.
We’ll dive deep into its workings, explore real-world scenarios, and unveil its magic!

Unveiling the Abstract Factory Pattern ✨

The Abstract Factory is a creational design pattern that furnishes an interface for creating families of related or dependent objects without specifying their concrete classes.

This pattern revolves around a super-factory, known as an Abstract Factory, which generates other factories. These factories, in turn, produce objects related to specific families.

When to Use the Abstract Factory Pattern?

🔸 Use this pattern when your code needs to interact with various families of related products, yet you prefer not to bind it to the concrete classes of those products.

🔸 It’s beneficial when providing a library of classes for users to extend.

Problem Scenario

Consider developing a native application intended for multiple operating systems like Windows, Mac, and Linux.
This application incorporates various UI elements such as buttons, inputs, text areas, and dialogs.

Rendering these UI elements in accordance with each operating system’s style poses a challenge.
Additionally, avoiding code alterations whenever a new OS is supported is crucial.

Solution

The Abstract Factory pattern suggests creating separate factory interfaces for each UI element type, like buttons, inputs, text areas, etc.
Each factory interface defines methods to create these UI elements, returning abstract products such as Button, Input, Textarea, etc.

Further, interfaces are declared for each distinct product of the UI elements, such as Button, Input, Textarea, etc., with common methods.

Concrete factories implement these interfaces and produce concrete products like WindowsButton, WindowsInput, WindowsTextarea, MacButton, MacInput, MacTextarea, etc.

In this example, the Abstract Factory pattern is utilized to create UI elements for Windows and Mac operating systems.

🔹 The Abstract Factory interface declares factory methods to create UI elements like createButton, createInput, createTextarea, etc.

🔹 Concrete factories implement these factory methods to create OS-specific UI elements.

🔹 Concrete products implement UI element interfaces like Button, Input, Textarea, etc.

🔹 Client code utilizes the Abstract Factory interface to create UI elements without knowledge of their concrete classes.

🔹 All UI elements are treated uniformly by client code, regardless of their concrete class.

🔹 Client code is aware of the render method in all UI elements but doesn’t concern itself with how it’s implemented in each concrete class.

How does it Work?

1️⃣ Concrete products implement UI element interfaces like Button, Input, Textarea, etc.

2️⃣ Concrete factories implement the Abstract Factory interface, producing concrete products tailored to specific OSes.

3️⃣ The Abstract Factory interface declares factory methods for creating UI elements like createButton, createInput, createTextarea, etc.

4️⃣ Abstract factories spawn concrete factories that adhere to the Abstract Factory interface.

💡 Note: The abstract factory can optimize by reusing existing objects rather than creating new ones every time.

Why Embrace the Abstract Factory?

🔮 Ensures compatibility among products obtained from a factory.

🔮 Avoids tight coupling between concrete products and client code.

🔮 Facilitates seamless replacement of the entire product family by switching concrete factories.

🔮 Enables introducing new product variants without disrupting existing client code.

🔮 Aligns with the Open/Closed Principle by extending the codebase with new classes instead of modifying existing ones.

🔮 Adheres to the Single Responsibility Principle by consolidating product creation in one place, enhancing code maintainability.

Show me the code

# GUIFactory “Interface”
# (since interfaces do not exist in Ruby, we will use a module to define the interface)
module GUIFactory
def create_button
raise NotImplementedError, #{self.class} has not implemented method #{__method__}‘”
end

def create_checkbox
raise NotImplementedError, #{self.class} has not implemented method #{__method__}‘”
end
end

class WinFactory
include GUIFactory

def create_button
WindowsButton.new
end

def create_checkbox
WindowsCheckbox.new
end
end

class MacFactory
include GUIFactory

def create_button
MacButton.new
end

def create_checkbox
MacCheckbox.new
end
end

# Checkbox
module Checkbox
def render
raise NotImplementedError, #{self.class} has not implemented method #{__method__}‘”
end

def on_change
raise NotImplementedError, #{self.class} has not implemented method #{__method__}‘”
end
end

class MacCheckbox
include Checkbox

def render
puts MacCheckbox render
end

def on_change
puts MacCheckbox on_change
end
end

class WindowsCheckbox
include Checkbox

def render
puts WindowsCheckbox render
end

def on_change
puts WindowsCheckbox on_change
end
end

# Button
module Button
def render
raise NotImplementedError, #{self.class} has not implemented method #{__method__}‘”
end

def on_click
raise NotImplementedError, #{self.class} has not implemented method #{__method__}‘”
end
end

class MacButton
include Button

def render
puts MacButton render
end

def on_click
puts MacButton on_click
end
end

class WindowsButton
include Button

def render
puts WindowsButton render
end

def on_click
puts WindowsButton on_click
end
end

# Application
class Application
def initialize(factory)
@factory = factory
end

def create_ui
button = @factory.create_button
checkbox = @factory.create_checkbox
button.render
checkbox.render
end
end

# Client
def main
op_system = mac

if op_system == win
factory = WinFactory.new
else
factory = MacFactory.new
end

app = Application.new(factory)
app.create_ui
end

# Output
MacButton render
MacCheckbox render

Conclusion

The Abstract Factory pattern furnishes an interface for creating families of related or dependent objects without specifying their concrete classes.

🔺 This pattern is advantageous when your code interacts with various families of related products without relying on their concrete classes.

🔺 It’s beneficial when providing a library of classes for users to extend.

Join the Quest!

Design Patterns: Elements of Reusable Object-Oriented Software
Abstract Factory Pattern
Code example

💻 You can find this and other design patterns here 📚

Leave a Reply

Your email address will not be published. Required fields are marked *