This chapter covers the Abstract Factory pattern.
GoF Definition
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
The Abstract Factory pattern will make more sense to you if you understand the Simple Factory pattern (Chapter 24) and the Factory Method pattern (Chapter 4). The Simple Factory pattern does not fall directly into the Gang of Four design patterns, so the discussion of that pattern appears in Part II of the book. I suggest that you read Chapters 4 and 24 before jumping into this one.
Concept
An abstract factory is often referred to as a factory of factories . This pattern provides a way to encapsulate a group of individual factories that have a common theme. In this process, you do not instantiate a class directly; instead, you instantiate a concrete factory and, after that, create products using the factory.
This pattern suits best when products are similar, but the product families are different (for example, a domestic dog is quite different from a wild dog). This pattern helps you to interchange specific implementations without changing the code that uses them, even at runtime. However, it may result in unnecessary complexity and extra work. Even debugging becomes tough in some cases.
Real-World Example
Suppose that you are decorating your room with two different types of tables; one is made of wood and the other one of steel. For the wooden type, you need to visit a carpenter, and for the other type, you may need to go to a metal shop. All of these are table factories. So, based on demand, you decide what kind of factory you need.
Computer-World Example
ADO.NET implements similar concepts to establish a connection to a database.
Implementation
I follow a similar structure in this chapter’s implementation. In this example, there are two types of animals: pet animals and wild animals. Program.cs is the client who is looking for some animals (which are wild dogs, pet dogs, wild tigers, and pet tigers in this case). You explore the construction processes of both pet animals and wild animals in this implementation.
IAnimalFactory: Abstract factory
WildAnimalFactory : A concrete factory that implements IAnimalFactory; it creates wild dogs and wild tigers
PetAnimalFactory : A concrete factory that implements IAnimalFactory, but this factory creates pet dogs and pet tigers
ITiger and IDog: Abstract products
PetTiger, PetDog, WildTiger, and WildDog: The concrete products. PetTiger and WildTiger implement the ITiger interface. PetDog and WildDog implement the IDog interface. The IDog and ITiger interfaces have only one method, AboutMe(), which is used in both the Simple Factory pattern and Factory Method pattern.
- A static class called FactoryProvider is used in the client code as follows:// Making a wild dog and wild tiger through// WildAnimalFactoryIAnimalFactory animalFactory = FactoryProvider.GetAnimalFactory("wild");IDog dog = animalFactory.GetDog();ITiger tiger = animalFactory.GetTiger();dog.AboutMe();tiger.AboutMe();
From the bold line in the previous code segment, you can see that I’m not directly instantiating the factory instance; instead, I’m using the FactoryProvider static class to get the factory instance. (This class has a similar structure as to when you used the concrete factories in the Factory Method pattern.) FactoryProvider provides the appropriate factory based on the parameter passed inside GetAnimalFactory(...) method.
Class Diagram
Solution Explorer View
Demonstration 1
Output
Q&A Session
5.1 Both the IDog and ITiger interfaces contain methods that have the same names. For example, both interfaces contain the AboutMe() method . Is that mandatory?
No. You can use different names for your methods. Also, the number of methods can be different in these interfaces. However, in Chapter 24, I cover the Simple Factory pattern, and in Chapter 4, I cover the Factory Method pattern. In this chapter, I continued the examples, which is why I kept the same method.
5.2 What are the challenges of using an abstract factory like this?
Any change in the abstract factory forces you to propagate the modification to the concrete factories. Standard design philosophy suggests you to program to an interface, but not to an implementation. This is one of the key principles that developers should always keep in mind. In most scenarios, developers do not want to change their abstract factories.
Also, the overall architecture is complex, which is why debugging is very challenging in some cases.
5.3 How do you distinguish a Simple Factory pattern from a Factory Method pattern or an Abstract Factory pattern?
I discuss the differences between a Simple Factory pattern and a Factory Method pattern in the “Q&A Session” section in Chapter 4.
In short, with the Simple Factory pattern, you can separate the code that varies from the rest of the code (basically, you decouple the client code). This approach helps you to manage the code more easily. Another key advantage of this approach is that the client is unaware of how the objects are created. So, it promotes both security and abstraction.
However, this approach can violate the open-closed principle. You can overcome this drawback using the Factory Method pattern, which allows subclasses to decide how the instantiation process is completed. Put simply, you delegate the object creation to the subclasses that implement the factory method to create objects.
The abstract factory is basically a factory of factories. It creates a family of related objects, but it does not depend on the concrete classes. In this pattern, you encapsulate a group of individual factories that have a common theme. In this process, you do not instantiate a class directly; instead, you get a concrete factory (I used a provider for that) and, after that, create products using the factory.
Lastly, I tried to keep the examples simple. A factory method promotes inheritance, and its subclasses need to implement the factory method to create objects. The Abstract Factory pattern can promote object composition by creating related objects using the methods that are exposed in a factory interface. In the end, all the factories promote loose coupling by reducing the dependencies on concrete classes.