Understanding and Implementing Abstract Factory Pattern in C#
Rahul Singh
Principal Engineering Manager | Solution Architect | Microsoft MVP | CodeProject MVP | Author | Udemy Instructor
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:
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:
For simplicity, let's assume we have three manufacturers:
And there are two types of 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.