Understanding and Implementing Abstract Factory Pattern in C#

Understanding and Implementing Abstract Factory Pattern in C#

Introduction

This article aims to explain and demonstrate the implementation of the Abstract Factory Pattern in C#.

Background

The Abstract Factory Pattern is useful when the client needs to create objects that are related in some way. If you need to create a family of related or dependent objects, then you can use the Abstract Factory Pattern.

This pattern is particularly beneficial when the client doesn't know exactly what type of object to create. For example, imagine a showroom selling cellphones that receives a request for smartphones made by Samsung. In this case, we don't know the exact type of object (phone) to create, but we know that we are looking for a smartphone manufactured by Samsung. This information can be utilized if our design implements the Abstract Factory Pattern.

With this concept in mind, let’s go ahead and create a design that facilitates the creation of related objects. We will build a simple application to address the scenario we just discussed.

Using the Code

Let’s begin with the GoF (Gang of Four) representation of the Abstract Factory Pattern:

  • AbstractFactory: Declares an interface for operations that create abstract products.
  • ConcreteFactory: Implements the operations to create concrete product objects.
  • AbstractProduct: Declares an interface for a type of product object.
  • Product: Defines a product object to be created by the corresponding concrete factory. It implements the AbstractProduct interface.
  • Client: Uses interfaces declared by AbstractFactory and AbstractProduct classes.

Now, let’s focus on the problem at hand. We need to create an appropriate object containing information about a cell phone based on the user’s request for:

  1. The type of phone.
  2. The phone manufacturer.

For simplicity, let's assume we have three manufacturers:

  • Nokia
  • Samsung
  • HTC

And there are two types of phones:

  • Smart Phones
  • Dumb Phones

With this in mind, we can create three concrete factories (one for each manufacturer) and two sets of related products (one for smart phones and one for dumb phones).

Creating the Abstract Products

In our case, we need two abstract products: ISmart and IDumb.

interface IDumb
{
    string Name();
}

interface ISmart
{
    string Name();
}        

Creating the Concrete Products

Let’s create concrete products for IDumb:

class Asha : IDumb
{
    public string Name() => "Asha";
}

class Primo : IDumb
{
    public string Name() => "Guru";
}

class Genie : IDumb
{
    public string Name() => "Genie";
}        

Similarly, let’s create concrete products for ISmart:

class Lumia : ISmart
{
    public string Name() => "Lumia";
}

class GalaxyS2 : ISmart
{
    public string Name() => "GalaxyS2";
}

class Titan : ISmart
{
    public string Name() => "Titan";
}        

Creating the Abstract Factory

Now, we define the abstract factory interface, IPhoneFactory, which will handle the creation of ISmart and IDumb objects.

interface IPhoneFactory
{
    ISmart GetSmart();
    IDumb GetDumb();
}        

Creating the Concrete Factories

Now we create the concrete factories for each manufacturer:

class SamsungFactory : IPhoneFactory
{
    public ISmart GetSmart() => new GalaxyS2();
    public IDumb GetDumb() => new Primo();
}

class HTCFactory : IPhoneFactory
{
    public ISmart GetSmart() => new Titan();
    public IDumb GetDumb() => new Genie();
}

class NokiaFactory : IPhoneFactory
{
    public ISmart GetSmart() => new Lumia();
    public IDumb GetDumb() => new Asha();
}        

Creating the Client

We now have all the abstract product classes, concrete product classes, the abstract factory, and concrete factories. Let’s write the client that will use this hierarchy to create the products:

enum MANUFACTURERS
{
    SAMSUNG,
    HTC,
    NOKIA
}

class PhoneTypeChecker
{
    ISmart smartPhone;
    IDumb dumbPhone;
    IPhoneFactory factory;
    MANUFACTURERS manufacturer;

    public PhoneTypeChecker(MANUFACTURERS m)
    {
        manufacturer = m;
    }

    public void CheckProducts()
    {
        switch (manufacturer)
        {
            case MANUFACTURERS.SAMSUNG:
                factory = new SamsungFactory();
                break;
            case MANUFACTURERS.HTC:
                factory = new HTCFactory();
                break;
            case MANUFACTURERS.NOKIA:
                factory = new NokiaFactory();
                break;
        }

        Console.WriteLine($"{manufacturer}:\nSmart Phone: {factory.GetSmart().Name()}\nDumb Phone: {factory.GetDumb().Name()}");
    }
}

static void Main(string[] args)
{
    PhoneTypeChecker checker = new PhoneTypeChecker(MANUFACTURERS.SAMSUNG);
    checker.CheckProducts();
    Console.ReadLine();

    checker = new PhoneTypeChecker(MANUFACTURERS.HTC);
    checker.CheckProducts();
    Console.ReadLine();

    checker = new PhoneTypeChecker(MANUFACTURERS.NOKIA);
    checker.CheckProducts();
    Console.ReadLine();
}        

Now, we have a basic skeleton for the Abstract Factory Pattern. The concrete products here are simple and only return the product names, but they can be extended to include more information.

Class Diagram

Before we end, let's look at a class diagram for the classes we created, which can be used to map it with the GoF diagram.


Note: Please refer to the source code for implementation. Stepping through the code will help you better understand this concept.

Points of Interest

The Abstract Factory Pattern is very useful in GUI-based applications where we need to create related GUI components. The example here is just for understanding the pattern and may not represent the best design for a real-world "cell phone information system" (which would require ongoing additions of new classes). For experienced developers, this article may seem basic, but it could be helpful for beginners looking to understand this pattern.

要查看或添加评论,请登录

Rahul Singh的更多文章

社区洞察

其他会员也浏览了