06 Dec 2023



Advanced

These design patterns are all about class instantiation or object creation. These patterns can be further categorized into Class-creational patterns and object-creational patterns. While class-creation patterns use inheritance effectively in the instantiation process, object-creation patterns use delegation effectively to get the job done.

  • Creational patterns are design patterns in software development focus on the process of object creation.
  • They provide solutions for creating objects in a flexible, efficient, and reusable manner, allowing developers to manage the instantiation process of classes and structures within a system.
  • Creational patterns help ensure that the way objects are created is scalable, adaptable, and promotes good design practices in software development.

Let's go through each Creational design pattern with its definition, main components, and C# code implementation along with explanations.

1. Abstract Factory Pattern:

Definition:

Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.

Main Components:

  • AbstractFactory: Declares an interface for creating a family of products.
  • ConcreteFactory: Implements the AbstractFactory interface to create concrete product objects.
  • AbstractProduct: Declares an interface for a type of product object.
  • ConcreteProduct: Implements the AbstractProduct interface to create a concrete product.
  • Client: Uses only the interfaces declared by AbstractFactory and AbstractProduct classes.

C# Code Implementation:

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

// Concrete Factory 1
public class ConcreteFactory1 : IAbstractFactory
{
    public IAbstractProductA CreateProductA() => new ConcreteProductA1();
    public IAbstractProductB CreateProductB() => new ConcreteProductB1();
}

// Concrete Factory 2
public class ConcreteFactory2 : IAbstractFactory
{
    public IAbstractProductA CreateProductA() => new ConcreteProductA2();
    public IAbstractProductB CreateProductB() => new ConcreteProductB2();
}

// Abstract Product A
public interface IAbstractProductA
{
    string Display();
}

// Concrete Product A1
public class ConcreteProductA1 : IAbstractProductA
{
    public string Display() => "Product A1";
}

// Concrete Product A2
public class ConcreteProductA2 : IAbstractProductA
{
    public string Display() => "Product A2";
}

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

// Concrete Product B1
public class ConcreteProductB1 : IAbstractProductB
{
    public string Display() => "Product B1";
}

// Concrete Product B2
public class ConcreteProductB2 : IAbstractProductB
{
    public string Display() => "Product B2";
}

// Client
public class Client
{
    private readonly IAbstractProductA productA;
    private readonly IAbstractProductB productB;

    public Client(IAbstractFactory factory)
    {
        productA = factory.CreateProductA();
        productB = factory.CreateProductB();
    }

    public void DisplayProducts()
    {
        Console.WriteLine(productA.Display());
        Console.WriteLine(productB.Display());
    }
}

// Usage
var factory1 = new ConcreteFactory1();
var client1 = new Client(factory1);
client1.DisplayProducts();

Explanation:

The Abstract Factory Pattern provides an interface (IAbstractFactory) to create families of related products (IAbstractProductA and IAbstractProductB). Concrete factories (ConcreteFactory1 and ConcreteFactory2) implement this interface, producing concrete products (ConcreteProductA1, ConcreteProductB1, etc.). The client can work with the abstract interfaces, allowing flexibility to switch between different families of products.

2. Factory Method Pattern:

Definition:

Factory Method Pattern defines an interface for creating an object but lets subclasses alter the type of objects that will be created.

Main Components:

  • Creator: Declares the factory method, which returns an object of type Product. It may also provide a default implementation.
  • ConcreteCreator: Overrides the factory method to produce specific concrete products.
  • Product: Defines the interface of objects the factory method creates.
  • ConcreteProduct: Implements the Product interface.
  • Client: Uses the Creator to create objects.

C# Code Implementation:

// Product
public interface IProduct
{
    string Display();
}

// Concrete Product A
public class ConcreteProductA : IProduct
{
    public string Display() => "Product A";
}

// Concrete Product B
public class ConcreteProductB : IProduct
{
    public string Display() => "Product B";
}

// Creator
public interface ICreator
{
    IProduct FactoryMethod();
}

// Concrete Creator A
public class ConcreteCreatorA : ICreator
{
    public IProduct FactoryMethod() => new ConcreteProductA();
}

// Concrete Creator B
public class ConcreteCreatorB : ICreator
{
    public IProduct FactoryMethod() => new ConcreteProductB();
}

// Client
public class Client
{
    private readonly ICreator creator;

    public Client(ICreator creator)
    {
        this.creator = creator;
    }

    public void DisplayProduct()
    {
        var product = creator.FactoryMethod();
        Console.WriteLine(product.Display());
    }
}

// Usage
var creatorA = new ConcreteCreatorA();
var clientA = new Client(creatorA);
clientA.DisplayProduct();

Explanation:

The Factory Method Pattern allows a class (ConcreteCreatorA or ConcreteCreatorB) to delegate the responsibility of instantiating a specific product (ConcreteProductA or ConcreteProductB) to its subclasses. The client (Client) can work with the abstract interface (IProduct and ICreator), and the concrete class instantiation is deferred to the subclasses through the factory method.

3. Singleton Pattern:

Definition:

Singleton Pattern ensures that a class has only one instance and provides a global point to this instance.

Main Components:

  • Singleton: Defines a static method to get the instance of the class. The constructor is made private to prevent external instantiation.
  • Client: Uses the Singleton instance.

C# Code Implementation:

// Singleton
public sealed class Singleton
{
    private static Singleton instance;
    private static readonly object lockObject = new object();

    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            lock (lockObject)
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    }

    public void Display()
    {
        Console.WriteLine("Singleton Instance");
    }
}

// Client
public class Client
{
    public void UseSingleton()
    {
        var singleton = Singleton.Instance;
        singleton.Display();
    }
}

// Usage
var client = new Client();
client.UseSingleton();

Explanation:

The Singleton Pattern ensures that there is only one instance of the Singleton class. The Instance property is responsible for creating the instance if it does not exist yet. The lock statement is used to ensure thread safety during the creation of the instance. The client uses the Singleton instance through the Instance property.

4. Builder Pattern:

Definition:

Builder Pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations.

Main Components:

  • Director: Defines the order of building parts of the product.
  • Builder: Declares interfaces for building parts of the product.
  • ConcreteBuilder: Implements the Builder interface to construct and assemble parts of the product.
  • Product: Represents the complex object under construction.
  • Client: Initiates the construction of the product using the Director and Builder.

C# Code Implementation:

// Product
public class Product
{
    private readonly List<string> parts = new List<string>();

    public void AddPart(string part)
    {
        parts.Add(part);
    }

    public void Display()
    {
        Console.WriteLine("Product Parts: " + string.Join(", ", parts));
    }
}

// Builder
public interface IBuilder
{
    void BuildPartA();
    void BuildPartB();
    Product GetResult();
}

// Concrete Builder
public class ConcreteBuilder : IBuilder
{
    private readonly Product product = new Product();

    public void BuildPartA()
    {
        product.AddPart("Part A");
    }

    public void BuildPartB()
    {
        product.AddPart("Part B");
    }

    public Product GetResult()
    {
        return product;
    }
}

// Director
public class Director
{
    public void Construct(IBuilder builder)
    {
        builder.BuildPartA();
        builder.BuildPartB();
    }
}

// Client
public class Client
{
    public void UseBuilder()
    {
        var builder = new ConcreteBuilder();
        var director = new Director();

        director.Construct(builder);
        var product = builder.GetResult();

        product.Display();
    }
}

// Usage
var client = new Client();
client.UseBuilder();

Explanation:

The Builder Pattern separates the construction of a complex object (Product) from its representation. The Director class defines the construction process using a Builder interface. The ConcreteBuilder class implements this interface to construct the Product. The Client uses the Director and Builder to create the Product, which can have different representations based on the builder used.

5. Prototype Pattern:

Definition:

Prototype Pattern creates new objects by copying an existing object, known as the prototype.

Main Components:

  • Prototype: Declares an interface for cloning itself.
  • ConcretePrototype: Implements the Clone method to create a copy of itself.
  • Client: Creates new objects by asking the prototype to clone itself.

C# Code Implementation:

// Prototype
public interface IPrototype
{
    IPrototype Clone();
    void Display();
}

// Concrete Prototype
public class ConcretePrototype : IPrototype
{
    private readonly string data;

    public ConcretePrototype(string data)
    {
        this.data = data;
    }

    public IPrototype Clone()
    {
        return new ConcretePrototype(data);
    }

    public void Display()
    {
        Console.WriteLine("Data: " + data);
    }
}

// Client
public class Client
{
    public void UsePrototype()
    {
        var prototype = new ConcretePrototype("Original Data");
        var clone = prototype.Clone();

        prototype.Display();
        clone.Display();
    }
}

// Usage
var client = new Client();
client.UsePrototype();

Explanation:

The Prototype Pattern allows the creation of new objects by copying an existing prototype. The IPrototype interface declares the Clone method, and the ConcretePrototype class implements it to create a copy of itself. The client can create new objects by cloning the prototype, resulting in objects with the same structure but potentially different data.

software-design-patterns
creational-design-pattern
c#