Abstract Factory Design Pattern In C#

What is the Abstract Factory Design Pattern?

  • Pattern denotes a design, factory denotes the location of product production, and abstract denotes the concealment of certain information. Thus, a software design pattern known as the Abstract Factory Pattern offers a means of encapsulating a collection of distinct factories that have a common theme.
  • Put more succinctly, the Abstract Factory can be thought of as a super factory that produces other factories. The Factory of Factories is another name for this Abstract Factory.
  • This means that while the concrete factory classes handle the actual object production, the Abstract Factory design pattern offers an interface for building families of linked or dependent products.
  • When a system must be independent of the creation, composition, and representation of its results, this design can be helpful.
  • Parts of the Pattern for an Abstract Factory:

The components of the Abstract Factory Design Pattern are as follows:

  • AbstractFactory: An interface for operations that produce abstract products is declared by the AbstractFactory. This will serve as an interface for the processes that generate objects of the Abstract Product.
  • ConcreteFactory: ConcreteFactory carries out the processes necessary to produce concrete product objects. These classes offer implementations for the interface methods as well as the Abstract Factory interface. Concrete product objects can be created with the help of these concrete classes.
  • AbstractProduct: Describes an interface for a certain kind of product object. These are going to be products' creation interfaces. In this case, we must specify the Operations that a Product ought to have. ConcreteProduct implements the AbstractProduct interface. The classes listed above carry out the Abstract Product interface.
  • Client: The client uses the interfaces declared by the AbstractFactory and AbstractProduct classes. This class will build a family of products by utilizing our Abstract Factory and Abstract Product interfaces.
  • Example to Understand Abstract Factory Design Pattern using C#:

Let's explore a practical scenario to enhance our understanding of the abstract factory design pattern. To simplify the concept, we will begin by implementing an example and then juxtapose it with the UML representation of the Abstract Factory Design Pattern.

One specific program we aim to develop is a tool that showcases information about both cars and motorcycles. Our goal is to illustrate two distinct categories of motorcycles - Standard and Sport - alongside the characteristics of two unique car models - Standard and Sport. It is crucial to understand that sports cars and motorcycles belong to the Sports category, while standard cars and motorcycles belong to the Regular category. With this distinction in mind, let's proceed to explore the implementation of the C# Abstract Factory Design Pattern for this purpose.

Step 1: Creating Abstract Products

To generate conceptual items, it is essential to define interfaces. In this scenario, the creation of two interfaces, or abstract classes, is necessary for each type of abstract product as we aim to produce distinct categories of basic items, such as cars and bicycles. I will now proceed to establish these two interfaces.

ICar.cs (ProductB_abstract)

After generating ICar.cs to establish an interface, proceed by pasting the subsequent code snippet. This is intended for our forthcoming secondary abstract offering. The assortment of Car products should encompass a fundamental interface tailored to every distinct product. For this scenario, the ICar interface necessitates incorporation by both the SportsCar and RegularCar iterations within the Car product line. Below is the interface we have formulated, to be executed by the subclasses of the ICar interface, featuring a singular abstract method.

Step 2: Creating Concrete Products

The specific product instance to be manufactured by the corresponding factory must be specified at this point. Subsequently, in the tutorial section discussing the factory component, the process of generating the product instance by the factory will be elaborated upon. It is essential to note that the concrete product classes are required to adhere to the implementation of the Abstract Product Interface. In our case, the concrete product class should implement either the IBike or ICar interface. Therefore, in alignment with our requirements, let us create four distinct products: RegularCar, SportsCar, RegularBike, and SportsBike.

RegularBike.cs (ProductA1)

After generating a class file named RegularBike.cs, could you incorporate the subsequent code within it? The Bike product family encompasses the subsequent standard bicycle products. It provides the functionality for the GetDetails function and, as evident, adheres to the IBike interface.

RegularCar.cs (ProductA2)

Copy and paste the following code snippet into a class file named RegularCar.cs. The RegularCar products within the Car product family are as listed below. This class offers specific implementations for the GetDetails method while also adhering to the ICar interface.

SportsCar.cs (ProductB2)

Create a new class file named SportsCar.cs and paste the following code into it. The Car product range encompasses the sports car model detailed below, offering the GetDetails method implementations and adhering to the ICar interface.

Step 3: Creating Abstract Factory

In this scenario, it is necessary to establish an interface that defines the operations responsible for producing AbstractProduct instances. This interface will be known as IVehicleFactory for our illustration. Therefore, please insert the following code snippet into a class file named IVehicleFactory.cs. The class contains two distinct methods. The CreateCar function is designed to generate multiple versions of cars, while the CreateBike method is intended for creating different variations of bikes. It is crucial to understand that at this stage, we are solely outlining the methods and specifying the return types, which correspond to the abstract products ICar and IBike. Consequently, we will be able to instantiate various objects (i.e., instances of subclasses of ICar and IBike) from these child classes.

NOTE: The abstract factory class mentioned above creates a family of other factories. Let's move on and establish this kind of factory family.

Step 4: Creating Concrete Factories

To generate specific product instances, it is essential to develop specific classes that execute the required functions. These classes will define the functionality outlined in the two interface methods and the AbstractFactory interface. Within our illustration, we will establish two distinct classes: RegularVehicleFactory and SportsVehicleFactory as ConcreteFactory implementations.

RegularVehicleFactory.cs

The RegularVehicleFactory class that is concrete will be responsible for generating Regular Vehicle Concrete Products. In this specific case, the Concrete Products from the Regular Vehicle Factory are the Regular Car and Regular Bike. Initially, generate a new class file named RegularVehicleFactory.cs and proceed by copying and pasting the provided code snippet. It is important to note that although actual products (new RegularBike and new RegularCar) are created within the method, the functions of the Concrete Factory return abstract products, namely IBike and ICar. Consequently, the Regular Family encompasses a Concrete Factory that manufactures Regular Bike and Regular Car Products.

SportsVehicleFactory (ConcreteFactory2)

The SportsVehicleFactory class, which is a concrete implementation, is responsible for manufacturing Concrete Products related to sports vehicles. In this specific case, the Concrete Products being produced are the SportsBike and SportsCar. To get started, establish a new class file named SportsVehicleFactory.cs and then add the provided code. It is noticeable that the methods defined in the Concrete Factory's interface return abstract products (IBike and ICar), while the factory method itself creates instances of concrete products (new SportsBike and new SportsCar). Consequently, this concrete factory is capable of generating products belonging to the sports vehicle category, such as sports bikes and sports cars.

Example

namespace AbstractFactoryDesignPattern
{
    // The ConcreteFactory2 class
    // Concrete Factories produce a family of products that belong to a single variant. 
    // The following Concrete Factory Produces Sports Bike and Sports Car which are compatible
    // The signatures of the Concrete Factory's methods return an abstract product (IBike) and (ICar) 
    // while inside the method a concrete product (new SportsBike and new SportsCar) is instantiated.
    public class SportsVehicleFactory : IVehicleFactory
    {
        public IBike CreateBike()
        {
            return new SportsBike();
        }
        public ICar CreateCar()
        {
            return new SportsCar();
       }
    }
}

NOTE: In our example, the client can create families of linked or dependent objects using the interface provided by IVehicleFactory. Here are the RegularVehicleFactory and SportsVehicleFactory classes, which are two specific implementations of that interface. Bikes and cars are two distinct kinds of related item families that are produced by these two classes. The RegularVehicleFactory family includes the RegularCar and RegularBike classes. Conversely, the SportsVehicleFactory family includes the SportsCar and SportsBike classes.

Step 5: Client

The Client class implements the AbstractFactory and AbstractProduct interfaces to construct a set of interconnected objects. In our example, it serves as the entry point in the Programme class. Therefore, update the Main function in the Programme class as described below.

Example

using System;
namespace AbstractFactoryDesignPattern
{

    //Client Code
    class Program
    {
        public static void Main()
        {
            // Fetch the Regular Bike and Car Details
            // Creating RegularVehicleFactory instance
            IVehicleFactory regularVehicleFactory = new RegularVehicleFactory();
            //regularVehicleFactory.CreateBike() will create and return Regular Bike
            IBike regularBike = regularVehicleFactory.CreateBike();
            regularBike.GetDetails();
            //regularVehicleFactory.CreateCar() will create and return Regular Car
            ICar regularCar = regularVehicleFactory.CreateCar();
            regularCar.GetDetails();

            // Fetch the Sports Bike and Car Details Created
            // Creating SportsVehicleFactory instance 
            IVehicleFactory sportsVehicleFactory = new SportsVehicleFactory();
            //sportsVehicleFactory.CreateBike() will create and return Sports Bike
            IBike sportsBike = sportsVehicleFactory.CreateBike();
            sportsBike.GetDetails();
            //sportsVehicleFactory.CreateCar() will create and return Sports Car
            ICar sportsCar = sportsVehicleFactory.CreateCar();
            sportsCar.GetDetails();

            Console.ReadKey();
        }
    }
}

Output:

The code snippet below illustrates the styling for a placeholder element within a diagram. The diagram's background is styled with a linear gradient, giving it a modern and visually appealing look. The border-radius property rounds the corners of the diagram, adding a touch of elegance. Additionally, the padding ensures that the content within the diagram is well spaced out, while the margin provides adequate spacing around the diagram. The text-align property centers the content within the diagram for a neat presentation. The placeholder icon within the diagram is sized at 3rem, making it prominent and easy to identify. The placeholder text is styled with a color of #9ca3af and a font size of 1rem, ensuring readability and consistency in design.

Abstract Factory Design Pattern UML (Class) Diagram:

The styling code below demonstrates the use of a linear gradient background with a border radius of 12px. It includes padding of 40px, a margin of 20px at the top and bottom, and is centered within its container. The icon within the diagram has a font size of 3rem and a margin bottom of 10px. The text is styled in a color of #9ca3af with a font size of 1rem.

Let's examine each element that makes up the Abstract Factory Design Pattern:

  • Abstract Products: These will be the user interfaces used to create abstract products. In this case, we must specify the Operations that a Product ought to have. It is the interfaces for IBike.cs and ICar.cs in our example.
  • Concrete Products: The classes that implement the Abstract Product interface are referred to as Concrete Products. The Concrete Products in our example are the RegularBike, SportsBike, RegularCar, and SportsCar classes.
  • Abstract Factory: This will be an interface for processes that generate Abstract Product objects. In our example, it will be IVehicleFactory.
  • Concrete Factory: These classes provide implementations for the interface methods as well as the AbstractFactory interface. The concrete factory classes in our example are RegularVehicleFactory and SportsVehicleFactory.
  • Abstract Factory Design Pattern Real-Time Example in C#:

Let's explore a practical scenario to illustrate the detailed process of applying the Abstract Factory Design Pattern in C#. To showcase the details of various courses, we aim to develop a unified application. The two types of courses we intend to showcase are front-end and back-end courses. Furthermore, we aim to present the resources through which students can access these courses. These resources could be either offline or online. Now, let's delve into utilizing the Abstract Factory Design Pattern in C# to build this application.

Step 1: Creating Abstract Products

To generate Abstract Products, it is essential to define interfaces at this point in the code. As we are aiming to generate two distinct types of identifiable products such as courses and sources, we will need to formulate two interfaces or abstract classes for each type of abstract product. Here, I will be crafting two interfaces.

ICourse.cs

The provided code must be duplicated into an interface named ICourse.cs within the system. This interface will serve as one of the Abstract Products within the Course product family and should define a common structure for each distinct product. Specifically, all variants of Front-End and Back-End Courses must adhere to the ICourse interface. The interface consists of three abstract methods, which are outlined below, and will be implemented by the FrontEndCourse and BackEndCourse subclasses.

ISource.cs

Copy and insert the subsequent code snippet into an interface named ISource.cs. Our intention is to introduce this as our second conceptual offering. Each specific product under the ISource product series should possess a fundamental interface. In this scenario, both the online and offline variations of the Source products must incorporate the ISource interface. It is evident that we have employed only a single abstract function to define the subsequent interface. The Online and Offline derived classes of the ISource interface will execute that operation.

Step 2: Creating Concrete Products

The specific product instance, also known as the tangible product unit that will be manufactured by the corresponding specific factory, must be specified at this stage. It is crucial to note that these specific product categories must adhere to both the interface functions and the conceptual product interface. The particular product category in our illustration must adhere to either the ICourse or ISource interface. Consequently, we will create four distinct products - FrontEndCourse, BackEndCourse, Online, and Offline - as per our defined requirements.

FrontEndCourse.cs

In the FrontEndCourse.cs class file, insert the provided code snippet. The FrontEndCourse product is part of the Course product family and encompasses the implementations for all three ICourse interface methods, demonstrating full adherence to the ICourse interface requirements.

Step 3: Creating Abstract Factory

The design of the interface responsible for generating Abstract Product instances needs to be established at this point. In this scenario, it will be referred to as ISourceCourseFactory. Below is the snippet that should be inserted into a class file named ISourceCourseFactory.cs. The interface contains two functions: GetSource, which is used to produce various versions of Sources, and GetCourse, which is used to generate different types of courses.

We are simply indicating that the functions and their respective outputs will be abstract entities like ISource and ICourse in this definition. Consequently, we have the ability to create different instances (for example, instances of subclasses of ISource and ICourse) from the derived classes.

Step 4: Creating Concrete Factories

To construct tangible product instances, it is essential to develop specific factory classes that adhere to abstract factory interfaces. In this scenario, we will establish two concrete classes, namely OnlineSourceCourseFactory and OfflineSourceCourseFactory.

OnlineSourceCourseFactory .cs

The concrete class OnlineSourceCourseFactory is responsible for generating products related to Online Source and Course Concrete. In this specific scenario, the products associated with Online Source and Course Concrete are BackEndCourse and Online. Initially, you should establish a class file named OnlineSourceCourseFactory.cs and then incorporate the provided code snippet. It's worth noting that within the process, instances of concrete products (new Online and new BackEndCourse) are created, while the methods of the Concrete Factory return abstract products (ICourse) and (ISource). Consequently, the OnlineSourceCourse Family encompasses a Concrete Factory that produces Online Source and BackEndCourse Products.

Example

namespace AbstractFactoryRealTimeExample
{
    public class OnlineSourceCourseFactory : ISourceCourseFactory
    {
        public ISource GetSource()
        {
            return new Online();
        }
        public ICourse GetCourse()
        {
            return new BackEndCourse();
        }
    }
}

OfflineSourceCourseFactory.cs

Offline resources and courses can be generated by utilizing the OfflineSourceCourseFactory concrete class. In this scenario, the concrete products for offline sources and courses are exemplified by FrontEndCourse and Offline. Simply copy and paste the provided code snippet into the OfflineSourceCourseFactory.cs class file. The code demonstrates the instantiation of concrete products (new Offline and new FrontEndCourse) within the method. It is worth noting that the methods of the Concrete Factory return abstract products (ICourse and ISource). Consequently, the OfflineSourceCourse Family encompasses concrete factory produces for Offline Source and FrontEndCourse products.

NOTE: In our example, the client can create families of related or dependent objects using the interface provided by ISourceCourseFactory. Here, we have the OnlineSourceCourseFactory and OfflineSourceCourseFactory classes, which are two specific implementations of that interface. These two classes, Courses and Sources, produce two distinct kinds of connected object families. The OnlineSourceCourse class family includes the Online and BackEndCourse classes. However, the OfflineSourceCourse family includes the Offline and FrondEndCourse classes.

Step 5: Client

The Client class leverages the AbstractFactory and AbstractProduct interfaces to construct a set of interconnected objects. In this scenario, it specifically pertains to the Main method within the Programme class. Therefore, implement the specified adjustments to the Main function of the Programme class.

Example

using System;
namespace AbstractFactoryRealTimeExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // Fetch the Front End Course and Source Details
            // Creating OfflineSourceCourseFactory instance
            ISourceCourseFactory offlineSourceCourseFactory = new OfflineSourceCourseFactory();
            //offlineSourceCourseFactory.GetCourse() will create and return FrondEndCourse object
            var course = offlineSourceCourseFactory.GetCourse();
            Console.WriteLine("Front End Course and Source Details");
            Console.WriteLine(course.GetCourseName());
            Console.WriteLine(course.GetCourseFee());
            Console.WriteLine(course.GetCourseDuration());

        
            var source = offlineSourceCourseFactory.GetSource();
            Console.WriteLine(source.GetSourceName());

            
            Console.WriteLine("\n----------------------\n");
            Console.WriteLine("Back End Course and Source Details");
            ISourceCourseFactory onlineSourceCourseFactory = new OnlineSourceCourseFactory();
            course = onlineSourceCourseFactory.GetCourse();
            Console.WriteLine(course.GetCourseName());
            Console.WriteLine(course.GetCourseFee());
            Console.WriteLine(course.GetCourseDuration());
            source = onlineSourceCourseFactory.GetSource();
            Console.WriteLine(source.GetSourceName());

            Console.ReadKey();
        }
    }
}

Output:

The <style> code snippet defines the styling for a placeholder diagram. It includes a background with a linear gradient, border radius, padding, margin, and text alignment properties. The placeholder diagram consists of an icon with a specific font size and margin, as well as text in a defined color and font size. This CSS code is crucial for creating visually appealing and structured placeholder elements on a webpage.

When to use Abstract Factory Design Pattern?

The following situations call for the use of the Abstract Factory Design Pattern in applications:

  • Form Families of Related items: You must implement this limitation when you have a group of related items that are intended to be used in tandem.
  • Maintain Uniformity in Products: Only items from the same family should be used in your application to guarantee consistency and compatibility.
  • Decouple Concrete Implementations: The pattern facilitates the separation of the client code from the interfaces' concrete implementations, which it depends on.
  • Promote Product Family Exchanges: Your application must be adaptable enough to function with several related product families and quickly switch between them.
  • Provide a Layer of Abstraction: When producing complex items requires a high degree of abstraction. This is particularly helpful for systems whose creation calls for configuration or dependency injection.
  • Support Scalability and configurability: Scalability and configurability are necessary for your application to enable the introduction of new product families without changing the client code already in place.
  • Platform-Specific Implementations: When your program must work with multiple environments or platforms, each of which has a particular set of products that must be implemented.
  • Mocking and Testing Made Easier: Abstract Factory can generate mock objects for unit testing, which simplifies the process of testing individual components.
  • Differences Between Abstract Factory and Factory Method Design Pattern:

Even though they both fall under the category of creational design patterns, they are implemented in diverse scenarios and possess distinct characteristics. Understanding these differences is crucial for choosing the right pattern to address a specific design problem.

Abstract Factory Design Pattern

  • Intent: Provides users the option to create families of dependent or linked items without having to declare their specific classes through an interface.
  • Abstractness: Deals with a collection of related goods (families) intended for shared usage.
  • Implementation: Consists of several manufacturing techniques, each of which usually yields a distinct kind of product. Typically, the Abstract Factory defines several ways to produce various types of items.
  • Use Case: This is employed when the system must function regardless of the manufacturing process of the goods it interacts with.
  • Flexibility: Greater flexibility because it can simultaneously produce several related object types.
  • Complexity: This method is more complex than the factory method because it has an extra abstraction layer for families of items.
  • Factory Method Design Pattern

  • Intent: This class provides an object creation interface but allows subclasses to select which class to instantiate. A class can delegate subclass instantiation to them using the Factory Method.
  • Abstractness: Focuses on a single product, yet it may come in multiple varieties. The creation of objects of a single type is the focus of the Factory Method pattern.
  • Implementation: This method uses a single manufacturing process to produce a single kind of good. Subclasses override the method to alter the kind of product produced.
  • Use Case: This is used when a class wants the objects it creates to be specified by its subclasses. It is also frequently used when a class is unable to predict the class of objects it must create.
  • Flexibility: Because it works with particular items, it is less flexible and more clear than Abstract Factory.
  • Complexity: Because there is only one product to focus on, it is easier to implement and comprehend.
  • Key Differences Between Abstract Factory and Factory Method

  • Scope of Creation: The factory Method creates a single product, whereas the Abstract Factory manages the creation of several connected or dependent goods.
  • Level of Abstraction: Because Abstract Factory deals with product families, it employs a higher level of abstraction.
  • Control Over Creation: In the Abstract Factory, creation is accomplished through object composition, whereas in the Factory Method, it is accomplished through inheritance (subclassing).
  • Use Cases: Use the factory method when working with single items; otherwise, use the Abstract Factory when you have a large number of objects that can be grouped.
  • Conclusion:

In summary, the C# Abstract Factory Design Pattern provides a way to encapsulate the creation of related object groups without the need to specify their specific classes. Creating interfaces for both factories and products promotes a decoupling between client code and the implementations, enhancing adaptability and ease of maintenance.

In general, the Abstract Factory Design Pattern proves to be a valuable asset in ensuring the organization, scalability, and manageability of code while working on projects that require creating groups of interconnected objects.

Input Required

This code uses input(). Please provide values below: