Builder vs factory vs Abstract Factory | C# Examples | 2023

Software design patterns play a crucial role in developing well-structured and maintainable code. Among the most commonly used design patterns are the Builder, Factory, and Abstract Factory patterns.

While they all belong to the creational design pattern category, each pattern serves a different purpose and exhibits distinct characteristics.

The builder pattern is employed when the construction of complex objects requires multiple steps, allowing for flexible creation and customization. The factory pattern, on the other hand, focuses on creating objects of a specific type without exposing the instantiation logic to the client. Finally, the abstract factory pattern provides an interface for creating families of related or dependent objects, ensuring their compatibility and interchangeability.

In this article, we will explore the differences between Builder, Factory, and Abstract Factory, highlighting their individual use cases, benefits, and implementation approaches.

Difference between builder vs factory vs Abstract Factory
Difference between builder vs factory vs Abstract Factory

What is a builder Pattern?

The Builder pattern is used to construct complex objects step by step while maintaining control over the object’s construction process. It is particularly useful when dealing with objects that have multiple optional parameters or configurations.

Core Components

  • Director: Controls the construction process using a builder object.
  • Builder: Abstract interface or class that defines the steps to create the object.
  • Concrete Builder: Implements the Builder interface and provides specific implementations for constructing the object.
  • Product: Represents the complex object being built.

Real-World Example

Consider a scenario where you are building a car. The Builder pattern allows you to define a CarBuilder with methods to set various optional features such as color, engine type, and navigation system.

The Director, in this case, would be responsible for executing the builder’s methods in a specific order to construct the car. The final product is a fully customized car with only the selected features.

using System;

// Product class
class Car
{
    public string Color { get; set; }
    public string Engine { get; set; }
    public bool HasNavigation { get; set; }

    public void Display()
    {
        Console.WriteLine($"Car Details: Color - {Color}, Engine - {Engine}, Navigation - {(HasNavigation ? "Yes" : "No")}");
    }
}

// Builder interface
interface ICarBuilder
{
    void SetColor(string color);
    void SetEngine(string engine);
    void SetNavigation(bool hasNavigation);
    Car GetCar();
}

// Concrete builder
class CarBuilder : ICarBuilder
{
    private Car car;

    public CarBuilder()
    {
        car = new Car();
    }

    public void SetColor(string color)
    {
        car.Color = color;
    }

    public void SetEngine(string engine)
    {
        car.Engine = engine;
    }

    public void SetNavigation(bool hasNavigation)
    {
        car.HasNavigation = hasNavigation;
    }

    public Car GetCar()
    {
        return car;
    }
}

// Director class
class CarDirector
{
    public Car ConstructSportsCar(ICarBuilder builder)
    {
        builder.SetColor("Red");
        builder.SetEngine("V8");
        builder.SetNavigation(false);

        return builder.GetCar();
    }

    public Car ConstructSUV(ICarBuilder builder)
    {
        builder.SetColor("Blue");
        builder.SetEngine("V6");
        builder.SetNavigation(true);

        return builder.GetCar();
    }
}

class Program
{
    static void Main()
    {
        CarDirector director = new CarDirector();
        CarBuilder builder = new CarBuilder();

        Car sportsCar = director.ConstructSportsCar(builder);
        sportsCar.Display();

        Car suv = director.ConstructSUV(builder);
        suv.Display();

        Console.ReadLine();
    }
}

Benefits

  • Provides a flexible and fluent API for constructing complex objects.
  • Allows step-by-step construction, enabling control over object creation.
  • Supports the creation of different variations of objects using the same building process.

What is Factory Pattern?

The Factory pattern focuses on creating objects without exposing the object instantiation logic to the client. It provides a centralized method or class responsible for creating and returning instances of related classes based on a given input or condition

I have already published a detailed article on factory patterns. Read Here If you want to deep dive into that.

Core Components

  • Creator: Abstract interface or class that declares the factory method(s).
  • Concrete Creator: Implements the factory method(s) and produces the specific instances.
  • Product: Represents the objects created by the factory.

Real-World Example

Suppose you have a software application that generates various documents, such as reports, invoices, and letters. The Factory pattern allows you to define a DocumentFactory that takes a document type as input and returns the corresponding document object.

The client code only needs to interact with the factory, abstracting away the specific document creation logic.

using System;

// Product interface
interface IDocument
{
    void Open();
    void Close();
}

// Concrete products
class Report : IDocument
{
    public void Open()
    {
        Console.WriteLine("Opening Report");
    }

    public void Close()
    {
        Console.WriteLine("Closing Report");
    }
}

class Invoice : IDocument
{
    public void Open()
    {
        Console.WriteLine("Opening Invoice");
    }

    public void Close()
    {
        Console.WriteLine("Closing Invoice");
    }
}

class Letter : IDocument
{
    public void Open()
    {
        Console.WriteLine("Opening Letter");
    }

    public void Close()
    {
        Console.WriteLine("Closing Letter");
    }
}

// Factory class
class DocumentFactory
{
    public IDocument CreateDocument(string documentType)
    {
        IDocument document;

        switch (documentType.ToLower())
        {
            case "report":
                document = new Report();
                break;
            case "invoice":
                document = new Invoice();
                break;
            case "letter":
                document = new Letter();
                break;
            default:
                throw new ArgumentException("Invalid document type.");
        }

        return document;
    }
}

class Program
{
    static void Main()
    {
        DocumentFactory factory = new DocumentFactory();

        IDocument report = factory.CreateDocument("report");
        report.Open();
        report.Close();

        IDocument invoice = factory.CreateDocument("invoice");
        invoice.Open();
        invoice.Close();

        IDocument letter = factory.CreateDocument("letter");
        letter.Open();
        letter.Close();

        Console.ReadLine();
    }
}

Benifits

  • Encapsulates object creation logic, promoting loose coupling between the client and the created objects.
  • Simplifies object creation by providing a centralized and consistent way to instantiate objects.
  • Enables easy extension by adding new concrete classes to the factory without modifying client code.

What is Abstract Factory Pattern?

The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes. It allows the client to create objects that belong to the same family and ensures that all the created objects are compatible.

I have already published a detailed article on Abstract Factory patterns. Read Here If you want to deep dive into that.

Core Components

  • Abstract Factory: Declares the creation methods for different types of related objects.
  • Concrete Factory: Implements the creation methods declared in the Abstract Factory, producing concrete objects of a specific family.
  • Abstract Product: Declares the interface for a group of related products.
  • Concrete Product: Implements the Abstract Product interface, representing a specific product of the family.

Real-World Example

Consider a graphical user interface (GUI) framework that supports multiple operating systems. The Abstract Factory pattern allows you to define an abstract GUIFactory with methods to create UI components such as buttons and checkboxes.

Concrete implementations of GUIFactory, like WindowsGUIFactory and MacGUIFactory, provide the actual creation logic for these components based on the specific operating system.

This ensures that all UI components within a particular family are consistent.

using System;

// Abstract Product A
interface IButton
{
    void Render();
}

// Concrete Product A1
class WindowsButton : IButton
{
    public void Render()
    {
        Console.WriteLine("Rendering a Windows button.");
    }
}

// Concrete Product A2
class MacButton : IButton
{
    public void Render()
    {
        Console.WriteLine("Rendering a Mac button.");
    }
}

// Abstract Product B
interface ICheckbox
{
    void Render();
}

// Concrete Product B1
class WindowsCheckbox : ICheckbox
{
    public void Render()
    {
        Console.WriteLine("Rendering a Windows checkbox.");
    }
}

// Concrete Product B2
class MacCheckbox : ICheckbox
{
    public void Render()
    {
        Console.WriteLine("Rendering a Mac checkbox.");
    }
}

// Abstract Factory
interface IGUIFactory
{
    IButton CreateButton();
    ICheckbox CreateCheckbox();
}

// Concrete Factory for Windows
class WindowsGUIFactory : IGUIFactory
{
    public IButton CreateButton()
    {
        return new WindowsButton();
    }

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

// Concrete Factory for Mac
class MacGUIFactory : IGUIFactory
{
    public IButton CreateButton()
    {
        return new MacButton();
    }

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

// Client code
class Client
{
    private IButton _button;
    private ICheckbox _checkbox;

    public Client(IGUIFactory factory)
    {
        _button = factory.CreateButton();
        _checkbox = factory.CreateCheckbox();
    }

    public void RenderUI()
    {
        _button.Render();
        _checkbox.Render();
    }
}

// Usage example
class Program
{
    static void Main()
    {
        // Create a Windows GUI
        IGUIFactory windowsFactory = new WindowsGUIFactory();
        Client windowsClient = new Client(windowsFactory);
        windowsClient.RenderUI();

        Console.WriteLine();

        // Create a Mac GUI
        IGUIFactory macFactory = new MacGUIFactory();
        Client macClient = new Client(macFactory);
        macClient.RenderUI();
    }
}

Conclusion

In conclusion, the Builder, Factory, and Abstract Factory patterns are all creational design patterns that serve different purposes in software development.


Frequently Asked Questions

Scroll to Top