Abstract Factory in C#
Tags: beginners, programming, tutorial, csharp
Published: 2022-09-24 18:11:10

The **Abstract Factory** is a creational design pattern taht lets us produce families of related objects without specifying their concrete classes.

The **Abstract Factory** is a design pattern used to provide an interface for creating a group of factories with a common use case without specifying the concrete classes.  Each concrete implementation of the abstract factory creates concrete objects that are part of the same family of products.

When using this pattern, we create factories which return different types of related objects. This pattern enables larger architectures such as Dependency Injection.

You can find the example code of this post, on [GitHub](https://github.com/Kalkwst/DesignPatterns)

## Conceptualizing the Problem

Imagine that we are creating a simulation application for a furniture-selling store. Our code consists of classes that either represent a family of products, such as `Armchairs`, `Sofas` and `Cupboards`, or variants of those families, such as `Rustic`, `Urban Collective` and `Scandinavian Contemporary`.

We need a way to create individual furniture objects so that tey match other objects of the same family. After all, customers can get quite upset if they receive non-matching furniture. Moreover, we don't want to change existing code when adding new products or families of products to the program.

The first thing the Abstract Factory pattern suggests, is to explicilty declare interfaces for each distinct product of the product family. Then we can make all variants of products follow the same interfaces. For example, all armchair variants can implement the `Armchair` interface; all sofa variants can implement the `Sofa` interface, and so on.

![All variants of the same object must be moved to a single class hierarchy.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ue1nsinhf48cvvoq5s02.PNG)

The next thing we are doing is to declare the *Abstract Factory*, an interface with a list of creation methods for all products that are part of the product family. The methods must return **abstract** product types represented by the interfaces we extracted previously: `Armchair`, `Sofa`, `Cupboard`, and so on.

![Each concrete factory corresponds to a specific product variant.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6em2spq0c1uorz9g4txz.PNG)

Let's now consider the product variants. For each variant of a product family, we create a separate factory class based on the `IFurnitureFactory` interface. A factory is a class that returns products of a particular king. For example, the `RusticFurnitureFactory` can only create `RusticArmchair`, `RusticSofa` and `RusticCupboard` objects.

The client code has to work with both factories and products via their respective abstract interfaces. This lets us change the type of a factory that we pass to the client code, as well as the product variant that the client code receives, without breaking the actual client code. 

Say the client wants a factory to produce a chair. The client doesn't have to be aware of the factory's class, nor does it matter what kind of chair it gets. Whether it's a Scandinavian Contemporary armchair or an Urban Collective armchair, the client must treat all chairs in the same manner, using the abstract `IArmchair` interface. With this approach, the client only knows that the product implements the `SitOn` method in some way. Also, whichever variant of the chair is returned, it'll always match the type of sofa or cupboard produced by the same factory object.

There's is a last thing to clarify: who creates the actual factory objects? Usually, the application creates a concrete factory object at the initialization stage. Just before that, the application must select the factory type depending on the configuration or the environment settings.

## Structuring the Abstract Factory Pattern

In its base implementation, the Abstract Factory pattern has five participants:

![Abstract Factory Class Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/79k31zttv6s2b3te3s7f.PNG)

- **Abstract Product**: The **Abstract Product** declares interfaces for a set of distinct but related products which make up a product family.
- **Concrete Product**: The **Concrete Products** are various implementations of abstract products, grouped by variants. Each abstract product (armchair/sofa) must be implemented in all given variants (Rustic/Scandinavian Contemporary).
- **Abstract Factory**: The **Abstract Factory** interface declares a set of methods for creating each of the abstract products.
- **Concrete Factory**: The **Concrete Factories** implement creation methods of the abstract factory. Each concrete factory corresponds to a specific variant of products and creates only those product variants.
- **Client**: The **Client** can work with any concrete factory/product variant, as long as it communicates with their objects via abstract interfaces. Although concrete factories instantiate concrete products, signatures of their creation methods must return corresponding *abstract* products. This way the client code that uses a factory doesn't get coupled to the specific variant of the product it gets from a factory.

To demonstrate how the Abstract Factory pattern works, we are going to create a cross-platform GUI component application.

To start we are first going to create our first product hierarchy, the `Button`. We will ceate an `IButton` interface, the **AbstractProduct** participant, and three buttons `LinuxButton`, `MacOSButton` and `WindowsButton`, the **ConcreteProducts**:

```csharp
namespace AbstractFactory.Buttons
{
    /// <summary>
    /// The Abstract Factory pattern assumes that we have several families
    /// of products, structured into separate class hierarchies (Button/Checkbox/Panel).
    /// All products of the same family have a common interface. 
    /// 
    /// This is the common interface for buttons family.
    /// </summary>
    public interface IButton
    {
        public void Paint();
    }
}
```

```csharp
namespace AbstractFactory.Buttons
{
    /// <summary>
    /// All product families have the same varieties (Linux/MacOS/Windows).
    /// 
    /// This is a Linux variant of a button.
    /// </summary>
    public class LinuxButton : IButton
    {
        public void Paint()
        {
            Console.WriteLine("Successfully created a LinuxButton");
        }
    }
}
```

```csharp
namespace AbstractFactory.Buttons
{
    /// <summary>
    /// All product families have the same varieties (Linux/MacOS/Windows).
    /// 
    /// This is a MacOS variant of a button.
    /// </summary>
    public class MacOSButton : IButton
    {
        public void Paint()
        {
            Console.WriteLine("Successfully created a MacOSButton");
        }
    }
}
```

```csharp
namespace AbstractFactory.Buttons
{
    /// <summary>
    /// All product families have the same varieties (Linux/MacOS/Windows).
    /// 
    /// This is a Windows variant of a button.
    /// </summary>
    public class WindowsButton : IButton
    {
        public void Paint()
        {
            Console.WriteLine("Successfully created a WindowsButton");
        }
    }
}
```

Now we will create another set of **Abstract Product** and **Concrete Products**, the checkboxes:

```csharp
namespace AbstractFactory.Checkboxes
{
    /// <summary>
    /// Checkboxes are the second product family.
    /// It has the same variants as buttons.
    /// </summary>
    public interface ICheckbox
    {
        public void Paint();
    }
}
```

```csharp
namespace AbstractFactory.Checkboxes
{
    /// <summary>
    /// All product families have the same varieties (Linux/MacOS/Windows)
    /// 
    /// This is a variant of a checkbox.
    /// </summary>
    public class LinuxCheckbox : ICheckbox
    {
        public void Paint()
        {
            Console.WriteLine("Successfully created a Linux checkbox");
        }
    }
}
```

```csharp
namespace AbstractFactory.Checkboxes
{
    /// <summary>
    /// All product families have the same varieties (Linux/MacOS/Windows)
    /// 
    /// This is a variant of a checkbox.
    /// </summary>
    public class MacOSCheckbox : ICheckbox
    {
        public void Paint()
        {
            Console.WriteLine("Successfully created a MacOS checkbox");
        }
    }
}
```

```csharp
namespace AbstractFactory.Checkboxes
{
    /// <summary>
    /// All product families have the same varieties (Linux/MacOS/Windows)
    /// 
    /// This is a variant of a checkbox.
    /// </summary>
    public class WindowsCheckbox : ICheckbox
    {
        public void Paint()
        {
            Console.WriteLine("Successfully created a WindowsCheckbox");
        }
    }
}
```

And the final product family, the Panels:

```csharp
namespace AbstractFactory.Panels
{
    /// <summary>
    /// Panels is the third product family. It has the same variants as buttons and checkboxes.
    /// </summary>
    public interface IPanel
    {
        public void Paint();
    }
}
```

```csharp
namespace AbstractFactory.Panels
{
    /// <summary>
    /// All product families have the same varieties (Linux/MacOS/Windows)
    /// 
    /// This is a variant of a panel.
    /// </summary>
    public class LinuxPanel : IPanel
    {
        public void Paint()
        {
            Console.WriteLine("Successfully created a LinuxPanel");
        }
    }
}
```

```csharp
namespace AbstractFactory.Panels
{
    /// <summary>
    /// All product families have the same varieties (Linux/MacOS/Windows)
    /// 
    /// This is a variant of a panel.
    /// </summary>
    public class MacOSPanel : IPanel
    {
        public void Paint()
        {
            Console.WriteLine("Successfully created a MacOSPanel");
        }
    }
}
```

```csharp
namespace AbstractFactory.Panels
{
    /// <summary>
    /// All product families have the same varieties (Linux/MacOS/Windows)
    /// 
    /// This is a variant of a panel.
    /// </summary>
    public class WindowsPanel : IPanel
    {
        public void Paint()
        {
            Console.WriteLine("Successfully created a WindowsPanel");
        }
    }
}
```

The next step is to create our **Abstract Factory** participant. We will create an interface for creating an `IButton`, an `ICheckbox` and an `IPanel`.

```csharp
using AbstractFactory.Buttons;
using AbstractFactory.Checkboxes;
using AbstractFactory.Panels;

namespace AbstractFactory.Factories
{
    /// <summary>
    /// Abstract factory knows about all (abstract) product types
    /// </summary>
    public interface IGUIFactory
    {
        public IButton CreateButton();
        public ICheckbox CreateCheckbox();
        public IPanel CreatePanel();
    }
}
```

Now we can start implementing the **Concrete Factory** participants:

```csharp
using AbstractFactory.Buttons;
using AbstractFactory.Checkboxes;
using AbstractFactory.Panels;

namespace AbstractFactory.Factories
{
    /// <summary>
    /// Each concrete factory extends basic factory and responsible for creating 
    /// products of a single variety
    /// </summary>
    public class LinuxFactory : IGUIFactory
    {
        public IButton CreateButton()
        {
            return new LinuxButton();
        }

        public ICheckbox CreateCheckbox()
        {
            return new LinuxCheckbox();
        }

        public IPanel CreatePanel()
        {
            return new LinuxPanel();
        }
    }
}
```

```csharp
using AbstractFactory.Buttons;
using AbstractFactory.Checkboxes;
using AbstractFactory.Panels;

namespace AbstractFactory.Factories
{
    /// <summary>
    /// Each concrete factory extends basic factory and responsible for creating 
    /// products of a single variety.
    /// </summary>
    public class MacOSFactory : IGUIFactory
    {
        public IButton CreateButton()
        {
            return new MacOSButton();
        }

        public ICheckbox CreateCheckbox()
        {
            return new MacOSCheckbox();
        }

        public IPanel CreatePanel()
        {
            return new MacOSPanel();
        }
    }
}
```

```csharp
using AbstractFactory.Buttons;
using AbstractFactory.Checkboxes;
using AbstractFactory.Panels;

namespace AbstractFactory.Factories
{
    /// <summary>
    /// Each concrete factory extends basic factory and responsible for creating 
    /// products of a single variety
    /// </summary>
    public class WindowsFactory : IGUIFactory
    {
        public IButton CreateButton()
        {
            return new WindowsButton();
        }

        public ICheckbox CreateCheckbox()
        {
            return new WindowsCheckbox();
        }

        public IPanel CreatePanel()
        {
            return new WindowsPanel();
        }
    }
}
```

The final step is to allow the **Client** application to get the appropriate products. The application doesn't care which concrete factory it will use. It just needs an interface to access the products:

```csharp
using AbstractFactory.Buttons;
using AbstractFactory.Checkboxes;
using AbstractFactory.Factories;
using AbstractFactory.Panels;

namespace AbstractFactory.App
{
    /// <summary>
    /// Factory users don't care which concrete factory they use since they work with
    /// factories and products through abstract interfaces.
    /// </summary>
    public class Application
    {
        private IButton button;
        private ICheckbox checkbox;
        private IPanel panel;

        public Application(IGUIFactory factory)
        {
            button = factory.CreateButton();
            checkbox = factory.CreateCheckbox();
            panel = factory.CreatePanel();
        }

        public void Paint()
        {
            button.Paint();
            checkbox.Paint();
            panel.Paint();
        }
    }
}
```

Finally, lets create our `Main()` method:

```csharp
using AbstractFactory.App;
using AbstractFactory.Factories;
using System.Runtime.InteropServices;

public class Program
{
    public static void Main(string[] args)
    {
        Application app = ConfigureApplication();
        app.Paint();
    }

    private static Application ConfigureApplication()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            return new Application(new LinuxFactory());
        if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            return new Application(new MacOSFactory());

        return new Application(new WindowsFactory());
    }
}
```

And the output of the application:

![Output of the exampe](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06jb416vb0i9cxy3zf6p.PNG)

## Pros and Cons of Factory Method Pattern
|||
|:--|:--|
|✔ We can be sure that the products you’re getting from a factory are compatible with each other. |❌ The code may become more complicated than it should be, since a lot of new interfaces and classes are introduced along with the pattern.|
|✔ We avoid tight coupling between concrete products and client code.||
|✔ We can extract the product creation code into one place, making the code easier to support, thus respecting the *Single Responsibility Principle*.||
|✔ We can introduce new variants of products without breaking existing client code, thus satisfying the *Open/Closed Principle*.||

## Relations with Other Patterns
-   Many designs start by using the **Factory Method** and evolve toward **Abstract Factory**, **Prototype** or **Builder**.
-  **Abstract Factory** classes are often based on a set of **Factory Methods**.
- **Builder** focuses on constructing complex object step by step. **Abstract Factory** specializes in creating families of related objects. *Abstract Factory* returns the product immediately, whereas *Builder* lets us run some additional construction steps before fetching the product.
- **Abstract Factory** can serve as an alternative to **Facade** when we only want to hide the way the subsystem objects are created from the client code.
- We can use the **Abstract Factory** along with the **Bridge** pattern. This pairing is useful when some abstractions defined by the *Bridge* can only work with specific implementations. In this case, *Abstract Factory* can encapsulate these relations and hide the complexity from the client code.

## Final Thoughts
In this article, we have discussed what is the Factory Method pattern, when to use it and what are the pros and cons of using this design pattern. We also examined the relations between Abstract Factory and other patterns.

The Abstract Factory is a very useful design pattern to implement when working with a family of objects that a factory creates.

If you need to abstract away the details of object creation, or if you need to be able to change the types of objects that are created at runtime, this is an ideal pattern to use. With some thought and planning, the Abstract Factory design pattern can be a valuable addition to your toolkit.

It's worth noting that the Abstract Factory pattern, along with the rest of the design patterns presented by the Gang of Four, is not a panacea or a be-all-end-all solution when designing an application. Once again it's up to the engineers to consider when to use a specific pattern. After all these patterns are useful when used as a precision tool, not a sledgehammer.