Abstract Factory Design Pattern in C#: Real-World Example and Code Explanations

The Abstract Factory Design Pattern is a creational design pattern that provides a way to create families of related or dependent objects without specifying their concrete classes. In this blog post, we will explore the Abstract Factory Design Pattern in C#, using a real-world example and providing code explanations.

Abstract-Factory-Design-Pattern-in-CSharp

What is the Abstract Factory Design pattern?

The Abstract Factory pattern is a creational design pattern that creates an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created. This pattern separates the process of creating objects from the objects themselves.

In other words: The Abstract Factory design pattern is a creational pattern that addresses the issue of creating entire groups of related products without specifying the exact classes of those products. This pattern defines an interface for creating different products, but the actual creation of the products is delegated to concrete factory classes. This allows for the creation of different product families without specifying the specific classes of those products.

Real-World Example Of Abstract Factory Design Pattern

Let’s imagine that you are building an application that needs to create different types of cars, such as sports cars and family cars. Here, each type of car can have different specifications and features, such as horsepower and the number of seats.

We can use the Abstract Factory pattern to implement this in C#, First, we need to create an interface for creating cars and then create subclasses for each type of car.

Example:

using System;
namespace AbstractFactoryExample
{ // Author: Shekh Ali

    // The 'ICarFactory' interface defines methods for creating objects
    // related to cars.
    interface ICarFactory
    {
        SportsCar CreateSportsCar();
        FamilyCar CreateFamilyCar();
    }

    // The 'SportsCar' class represents a sports car
    class SportsCar
    {
        public virtual void Drive()
        {
            Console.WriteLine("Driving a sports car");
        }
        public virtual void OpenSunRoof()
        {
            Console.WriteLine("Opening sunroof");
        }
    }

    // The 'FamilyCar' class represents a family car
    class FamilyCar
    {
        public virtual void Drive()
        {
            Console.WriteLine("Driving a family car");
        }
    }

    class MercedesSportsCar : SportsCar
    {
        public int Horsepower { get; set; }
        public int NumberOfSeats { get; set; }
        public string Color { get; set; }
        public string TransmissionType { get; set; }

        public override void OpenSunRoof()
        {
            Console.WriteLine("\n Opening sunroof of a Mercedes sports car");
        }

        public override void Drive()
        {
            Console.WriteLine($" Driving a {Color} Mercedes sports car with {Horsepower} horsepower with {NumberOfSeats} seats.");
            Console.WriteLine($" Transmission Type: {TransmissionType}");
        }
    }
    class MercedesFamilyCar : FamilyCar
    {
        public int NumberOfSeats { get; set; }
        public int NumberOfDoors { get; set; }
        public string Color { get; set; }
        public string TransmissionType { get; set; }

        public override void Drive()
        {
            Console.WriteLine("\n Driving a Mercedes family car with " + NumberOfSeats + " seats and " + NumberOfDoors + " doors.");
            Console.WriteLine($" Transmission Type: {TransmissionType}\n");
        }
    }


    class BMWSportsCar : SportsCar
    {
        // Add specific implementation for BMW sports car here
        public int Horsepower { get; set; }
        public int NumberOfSeats { get; set; }
        public string Color { get; set; }
        public string TransmissionType { get; set; }

        public override void OpenSunRoof()
        {
            Console.WriteLine("\n Opening sunroof of a BMW sports car");
        }

        public override void Drive()
        {
            Console.WriteLine($" Driving a {Color} BMW sports car with {Horsepower} horsepower with {NumberOfSeats} seats.");
            Console.WriteLine($" Transmission Type: {TransmissionType}");
        }
    }

    class BMWFamilyCar : FamilyCar
    {
        // Add specific implementation for BMW family car here
        public int NumberOfSeats { get; set; }
        public int NumberOfDoors { get; set; }
        public string Color { get; set; }
        public string TransmissionType { get; set; }

        public override void Drive()
        {
            Console.WriteLine($"\n Driving a {Color} BMW family car with {NumberOfSeats} seats and {NumberOfDoors} doors.");
            Console.WriteLine($" Transmission Type: {TransmissionType}\n");
        }
    }

    // The 'MercedesFactory' class creates objects related to Mercedes cars
    class MercedesFactory : ICarFactory
    {
        public SportsCar CreateSportsCar()
        {
            // Here, you can create an instance of the MercedesSportsCar class
            return new MercedesSportsCar()
            {
                Color = "Red",
                Horsepower = 1048,
                NumberOfSeats = 2,
                TransmissionType = "CVT transmission"
            };
        }

        public FamilyCar CreateFamilyCar()
        {
            // Here, you can create an instance of the MercedesFamilyCar class
            return new MercedesFamilyCar()
            {
                Color = "White",
                NumberOfDoors = 4,
                NumberOfSeats = 4,
                TransmissionType = "DCT transmission"
            };
        }
    }

    // The 'BMWFactory' class creates objects related to BMW cars
    class BMWFactory : ICarFactory
    {
        public SportsCar CreateSportsCar()
        {
            // Here, you can create an instance of the BMWSportsCar class
            return new BMWSportsCar()
            {
                Color = "Red",
                Horsepower = 1048,
                NumberOfSeats = 2,
                TransmissionType = "Automatic (TC)"
            };
        }

        public FamilyCar CreateFamilyCar()
        {
            // Here, you can create an instance of the BMWFamilyCar class
            return new BMWFamilyCar()
            {
                Color = "Black Sapphire Metallic",
                NumberOfDoors = 4,
                NumberOfSeats = 7,
                TransmissionType = "DCT transmission"
            };
        }
    }

    // This class represents a client that uses the factory
    class CarClient
    {
        private SportsCar _sportsCar;
        private FamilyCar _familyCar;

        public CarClient(ICarFactory factory)
        {
            _sportsCar = factory.CreateSportsCar();
            _familyCar = factory.CreateFamilyCar();
        }

        public void DriveCars()
        {
            _sportsCar.Drive();
            _familyCar.Drive();
        }
        public void OpenSunRoof()
        {
            _sportsCar.OpenSunRoof();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // The client code can work with any factory class.
            ICarFactory factory = new MercedesFactory();
            CarClient client = new CarClient(factory);
            client.OpenSunRoof();
            client.DriveCars();

            factory = new BMWFactory();
            client = new CarClient(factory);
            client.DriveCars();

            Console.ReadLine();
        }
    }
}

Output:

Abstract design Factory code example result
Output: Abstract Design Factory

Code Explanation:

So in this example, the ICarFactory interface is the abstract factory, SportsCar, and FamilyCar is the abstract products, MercedesSportsCar, MercedesFamilyCar, BMWSportsCar, and BMWFamilyCar are the concrete products, and MercedesFactory, and BMWFactory are concrete factories.

  • The ICarFactory interface defines methods for creating objects related to cars, specifically sports cars and family cars.
  • The SportsCar and FamilyCar classes are abstract classes that define the basic structure of a sports car and a family car. These classes are not concrete classes.
  • The MercedesSportsCar, MercedesFamilyCar, BMWSportsCar, BMWFamilyCar classes are concrete classes that inherit from the SportsCar and FamilyCar classes and provide a specific implementation for different types of cars. These classes are concrete classes.
  • The MercedesFactory and BMWFactory classes are concrete classes that implement the ICarFactory interface, they provide the implementation of the methods defined in the interface to create specific types of cars. These classes are concrete factory classes.
  • The CarClient class is a client class that uses the factory to create objects of cars, it doesn’t know the concrete classes of the objects that are created.
  • The Main method in the Program class uses the factory to create a CarClient object and then uses that object to drive the cars created by the factory.

The Abstract Factory design pattern is a creational pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. It allows the client to use any factory class without knowing the specific class of the objects being created. This pattern is useful for creating objects that need to work together as a group and for creating objects that are interchangeable within a larger system.

UML Class Diagram: Abstract Factory Pattern

The UML class diagram below shows how the code is organized, it may not be perfect for you but I hope the code itself is easy enough to understand.

Abstract Factory Design Pattern UML Diagram
Abstract Factory Design Pattern UML Diagram

Real-world scenarios where the Abstract Factory Design Pattern can be used

The following are a few examples of when the Abstract Factory pattern is used:

ExampleDescription
GUI toolkits:A GUI toolkit may provide a factory that creates buttons, checkboxes, and other widgets based on the platform that the application is running on.
Database access libraries:A library that provides a way to access different types of databases (Oracle, MySQL, SQL Server) may use a factory to create the appropriate connection and command objects for each database.
DbProviderFactory is a good example of an Abstract Factory pattern and each Create method of this factory is a FactoryMethod like CreateConnection, CreateCommand, and CreateDataAdapter. At the same time, each product such as DbConnection has a concrete product class which is SqlConnection and OralceConnection, etc.
Builders:A builder pattern could use an abstract factory to provide a way to create different types of products.
Automobile manufacturing:An automobile manufacturing company could use an abstract factory to provide a way to create different types of cars for different brands.
Product line:A company that produces several types of electronic devices, such as smartphones, tablets, and laptops, could use an abstract factory to create different products for different brands. For example, the factory could create a Samsung smartphone, an Apple tablet, and a Lenovo laptop. The client code would be able to use the factory to create any of these products without knowing the specific brand or model.

Summary

In summary, the Abstract Factory pattern should be used when:

  • A system needs to be independent of the way products are created, composed, and represented.
  • A system needs to be configured with one of the multiple families of products.
  • A family of related products should be designed to be used together.
  • A class library of products should be provided, revealing only their interfaces not implementations.
  • The construction process of a complex object should be separated from its representation.

The Abstract Factory design pattern can also be used in conjunction with other design patterns, such as Builder, Prototype, or Singleton to achieve specific design goals.

FAQ

The following is the list of a few frequently asked questions and answers about the Abstract Factory Design Pattern in C#.

Q: What is the Abstract Factory Design Pattern in C#?

The Abstract Factory Design Pattern is a creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes.

Q: How does the Abstract Factory Design Pattern work?

The Abstract Factory Design Pattern works by creating an interface for creating a group of related objects and then implementing concrete classes that implement this interface. These concrete classes then create and return specific objects belonging to the same family.

Q: What are the benefits of using the Abstract Factory Design Pattern in C#?

The benefits of using the Abstract Factory Design Pattern are the ability to easily change the implementation of a group of related objects without affecting the clients that use them, and also the ability to separate the implementation of objects from the client code that uses them.

Q: When should you use the Abstract Factory Design Pattern?

You should use the Abstract Factory Design Pattern when you need to create a group of related objects, and you want to be able to easily change the implementation of these objects without affecting the clients that use them.

We would love to hear your thoughts on this post. Please leave a comment below and share it with others.

Articles you might also like:

5 2 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments