Abstract Factory Design Pattern in .NET Core C# 12

Overview

It provides a structured method for creating related or dependent objects without specifying their concrete classes, making it a fundamental paradigm in software engineering. In order to illustrate how it can be applied in .NET Core, this article examines its core components—AbstractFactory, ConcreteFactory, AbstractProduct, ConcreteProduct, and Client—through intricate C# 12 implementation examples.

Understanding the Abstract Factory Pattern

 In the Abstract Factory pattern, a group of individual factories is encapsulated with a common theme without specifying their concrete classes. By adhering to the Open/Closed Principle, facilitates loose coupling between the client and the specific implementations of objects, enabling the introduction of new types of products without modifying the client code.

Components of the Abstract Factory Pattern

An AbstractFactory declares an interface for operations that create abstract products.

namespace AbstractFactoryImplementation;

// Abstract Factory interface
public interface IAbstractFactory
{
    IAbstractProductA CreateProductA();
    IAbstractProductB CreateProductB();
}

ConcreteFactory: Implements the operations to create concrete product objects.

namespace AbstractFactoryImplementation;
// Concrete Factory A
public class ConcreteFactoryA : IAbstractFactory
{
    public IAbstractProductA CreateProductA()
    {
        return new ConcreteProductA1();
    }
    public IAbstractProductB CreateProductB()
    {
        return new ConcreteProductB1();
    }

}
namespace AbstractFactoryImplementation;

// Concrete Factory B
public class ConcreteFactoryB : IAbstractFactory
{
    public IAbstractProductA CreateProductA()
    {
        return new ConcreteProductA2();
    }

    public IAbstractProductB CreateProductB()
    {
        return new ConcreteProductB2();
    }
}

The AbstractProduct interface defines a type of product object.

namespace AbstractFactoryImplementation;

// Abstract Product A
public interface IAbstractProductA
{
    string UsefulFunctionA();
}
namespace AbstractFactoryImplementation;

// Abstract Product B
public interface IAbstractProductB
{
    string UsefulFunctionB();
}

It implements the AbstractProduct interface and defines a product object to be created by the concrete factory.

namespace AbstractFactoryImplementation;

// Concrete Product A1
public class ConcreteProductA1 : IAbstractProductA
{
    public string UsefulFunctionA()
    {
        return "The result of the product A1.";
    }
}
namespace AbstractFactoryImplementation;
// Concrete Product A2
public class ConcreteProductA2 : IAbstractProductA
{
    public string UsefulFunctionA()
    {
        return "The result of the product A2.";
    }
}
namespace AbstractFactoryImplementation;

// Concrete Product B1
public class ConcreteProductB1 : IAbstractProductB
{
    public string UsefulFunctionB()
    {
        return "The result of the product B1.";
    }
}
namespace AbstractFactoryImplementation;

// Concrete Product B2
public class ConcreteProductB2 : IAbstractProductB
{
    public string UsefulFunctionB()
    {
        return "The result of the product B2.";
    }
}

Interfaces declared by AbstractFactory and AbstractProduct are used by the client.

namespace AbstractFactoryImplementation;

public class Client
{
    private readonly IAbstractProductA _abstractProductA;
    private readonly IAbstractProductB _abstractProductB;

    public Client(IAbstractFactory factory)
    {
        _abstractProductA = factory.CreateProductA();
        _abstractProductB = factory.CreateProductB();
    }

    public string ClientCode()
    {
        var result = $"Client uses {_abstractProductA.UsefulFunctionA()} and {_abstractProductB.UsefulFunctionB()}";
        return result;
    }
}

Implementation in C# 12

We will now use C# 12 to implement the Abstract Factory pattern:

using AbstractFactoryImplementation;

Console.WriteLine("Hello, from Ziggy Rafiq!");

// Client usage with Concrete Factory A
IAbstractFactory factoryA = new ConcreteFactoryA();
Client clientA = new Client(factoryA);
Console.WriteLine(clientA.ClientCode());

// Client usage with Concrete Factory B
IAbstractFactory factoryB = new ConcreteFactoryB();
Client clientB = new Client(factoryB);
Console.WriteLine(clientB.ClientCode());

Summary

By using the Abstract Factory pattern, software systems can be flexible and maintainable since families of related objects can be created without specifying their concrete classes. By leveraging interfaces, classes, and the language's dynamic capabilities, C# 12 ensures the pattern is implemented efficiently, adhering to modern design principles. .NET Core C# 12 developers can enhance code organization and scalability by applying the Abstract Factory pattern, paving the way for robust and scalable software solutions.

I have uploaded this article's code examples to my GitHub Repository. If you have enjoyed and liked this article please follow me on LinkedIn https://www.linkedin.com/in/ziggyrafiq/  and click the like button. Thank you for your support.


Similar Articles
Capgemini
Capgemini is a global leader in consulting, technology services, and digital transformation.