Abstract Factory Pattern | Creational Design Pattern

Abstract Factory Pattern

The Abstract Factory Pattern is a creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. It is a higher-level form of factory pattern that deals with multiple types of related objects at once.

Key components and concepts involved in the Abstract Factory Pattern:


1. Abstract Factory:

   - Declares an interface for creating a family of related objects. It typically includes a set of abstract methods, each responsible for creating a different type of product.


2. Concrete Factory:

   - Implements the Abstract Factory interface by providing concrete implementations for the creation methods. Each concrete factory is responsible for producing a family of related products.


3. Abstract Product:

   - Declares the interface for a type of product within the family of related products. Each abstract product corresponds to a creation method in the Abstract Factory.


4. Concrete Product:

   - Implements the Abstract Product interface, providing specific functionality for a particular product type. Concrete products are created by the concrete factories.


5. Client:

   - Uses the Abstract Factory and Abstract Product interfaces to work with families of related objects without being concerned about their concrete classes.


Example in C# to illustrate the Abstract Factory Pattern:


// Abstract Product A

interface IChair

{

    void SitOn();

}


// Concrete Product A1

class ModernChair : IChair

{

    public void SitOn()

    {

        Console.WriteLine("Sitting on a modern chair.");

    }

}


// Concrete Product A2

class VictorianChair : IChair

{

    public void SitOn()

    {

        Console.WriteLine("Sitting on a Victorian chair.");

    }

}


// Abstract Product B

interface ICoffeeTable

{

    void PutCoffee();

}


// Concrete Product B1

class ModernCoffeeTable : ICoffeeTable

{

    public void PutCoffee()

    {

        Console.WriteLine("Putting coffee on a modern coffee table.");

    }

}


// Concrete Product B2

class VictorianCoffeeTable : ICoffeeTable

{

    public void PutCoffee()

    {

        Console.WriteLine("Putting coffee on a Victorian coffee table.");

    }

}


// Abstract Factory

interface IFurnitureFactory

{

    IChair CreateChair();

    ICoffeeTable CreateCoffeeTable();

}


// Concrete Factory 1

class ModernFurnitureFactory : IFurnitureFactory

{

    public IChair CreateChair()

    {

        return new ModernChair();

    }


    public ICoffeeTable CreateCoffeeTable()

    {

        return new ModernCoffeeTable();

    }

}


// Concrete Factory 2

class VictorianFurnitureFactory : IFurnitureFactory

{

    public IChair CreateChair()

    {

        return new VictorianChair();

    }


    public ICoffeeTable CreateCoffeeTable()

    {

        return new VictorianCoffeeTable();

    }

}


// Client

class Client

{

    private IChair chair;

    private ICoffeeTable coffeeTable;


    public Client(IFurnitureFactory furnitureFactory)

    {

        chair = furnitureFactory.CreateChair();

        coffeeTable = furnitureFactory.CreateCoffeeTable();

    }


    public void SitAndPutCoffee()

    {

        chair.SitOn();

        coffeeTable.PutCoffee();

    }

}


// Usage

class Program

{

    static void Main()

    {

        // Client uses a modern furniture factory

        IFurnitureFactory modernFactory = new ModernFurnitureFactory();

        Client modernClient = new Client(modernFactory);

        modernClient.SitAndPutCoffee();


        // Client uses a Victorian furniture factory

        IFurnitureFactory victorianFactory = new VictorianFurnitureFactory();

        Client victorianClient = new Client(victorianFactory);

        victorianClient.SitAndPutCoffee();

    }

}


In this example, the Abstract Factory (`IFurnitureFactory`) declares methods for creating two families of related products: 

chairs and coffee tables.

Concrete factories (`ModernFurnitureFactory` and `VictorianFurnitureFactory`) implement these methods to create specific products from their respective families.


The client (`Client`) is constructed with an Abstract Factory, and it can use the products from that factory without needing to know their concrete classes. The client can easily switch between families of products by changing the injected factory.


This pattern promotes the creation of families of related or dependent objects in a way that ensures their compatibility. It's particularly useful when the system needs to be independent of how its objects are created, composed, and represented.


Real-world use case for the Abstract Factory Pattern can be found in the development of graphical user interface (GUI) libraries or frameworks. Let's consider a scenario where you're building a GUI library that needs to support multiple platforms, each having its own set of native components.


 Real-world Example: GUI Library for Multiple Platforms


1. Abstract Product A: `Button`

   - Declares an interface for a button in the GUI.


2. Concrete Products A1, A2: `WindowsButton`, `MacButton`

   - Implement the `Button` interface for specific platforms (Windows and macOS).


3. Abstract Product B: `TextBox`

   - Declares an interface for a text box in the GUI.


4. Concrete Products B1, B2: `WindowsTextBox`, `MacTextBox`

   - Implement the `TextBox` interface for specific platforms.


5. Abstract Factory: `GUIFactory`

   - Declares methods for creating abstract products (buttons and text boxes).


6. Concrete Factories 1, 2: `WindowsGUIFactory`, `MacGUIFactory`

   - Implement the `GUIFactory` interface to create concrete products for their respective platforms.


Example:


// Abstract Product A

interface Button

{

    void Render();

}


// Concrete Product A1

class WindowsButton : Button

{

    public void Render()

    {

        Console.WriteLine("Rendering Windows button");

    }

}


// Concrete Product A2

class MacButton : Button

{

    public void Render()

    {

        Console.WriteLine("Rendering Mac button");

    }

}


// Abstract Product B

interface TextBox

{

    void Display();

}


// Concrete Product B1

class WindowsTextBox : TextBox

{

    public void Display()

    {

        Console.WriteLine("Displaying Windows text box");

    }

}


// Concrete Product B2

class MacTextBox : TextBox

{

    public void Display()

    {

        Console.WriteLine("Displaying Mac text box");

    }

}


// Abstract Factory

interface GUIFactory

{

    Button CreateButton();

    TextBox CreateTextBox();

}


// Concrete Factory 1

class WindowsGUIFactory : GUIFactory

{

    public Button CreateButton()

    {

        return new WindowsButton();

    }


    public TextBox CreateTextBox()

    {

        return new WindowsTextBox();

    }

}


// Concrete Factory 2

class MacGUIFactory : GUIFactory

{

    public Button CreateButton()

    {

        return new MacButton();

    }


    public TextBox CreateTextBox()

    {

        return new MacTextBox();

    }

}


// Client

class GUIApplication

{

    private Button button;

    private TextBox textBox;


    public GUIApplication(GUIFactory guiFactory)

    {

        button = guiFactory.CreateButton();

        textBox = guiFactory.CreateTextBox();

    }


    public void Run()

    {

        button.Render();

        textBox.Display();

    }

}


// Usage

class Program

{

    static void Main()

    {

        // Client uses a Windows GUI factory

        GUIFactory windowsFactory = new WindowsGUIFactory();

        GUIApplication windowsApp = new GUIApplication(windowsFactory);

        windowsApp.Run();


        // Client uses a Mac GUI factory

        GUIFactory macFactory = new MacGUIFactory();

        GUIApplication macApp = new GUIApplication(macFactory);

        macApp.Run();

    }

}


In this example, the Abstract Factory Pattern allows you to create families of GUI components (buttons and text boxes) for different platforms (Windows and macOS). Clients can use these abstract factories without being concerned about the concrete classes of the products. This pattern helps achieve platform independence and ensures that the created GUI components are compatible with the specific platform they are intended for.






Comments