In the previous post, we discussed design patterns, their structure and usage. Then we discussed the three fundamental types and started off with the first one – Creational Patterns.
Continuing with creational patterns we will now discuss Abstract Factory pattern, which is considered to be a super set of Factory Method.
Abstract Factory
In the Factory method, we discussed how it targets a single family of subclasses using a corresponding set of factory method classes, or a single factory method class via parametrised/ static factory method. But if we target families of related classes (multiple abstractions having their own subclasses) and need to interact with them using a single abstraction then factory method will not work.
A good example could be the creation of doors and windows for a room. A room could offer a combination of wooden door, sliding door etc. and wooden window, glass window etc. The client machine will however, interact with a single abstraction (abstract factory) to create the desired door and window combination based on selection/ configuration. This could be a good candidate for an Abstract Factory.
So abstract factory allows initiation of families (plural) of related classes using a single interface (abstract factory) independent of the underline concrete classes.
Reasons
When a system needs to use families of related or dependent classes, it might need to instantiate several subclasses. This will lead to code duplication and complexities. Taking the above example of a room, the client machine will need to instantiate classes for doors and windows for one combination and then do the same for others, one by one. This will break the abstraction of those classes, exposes their encapsulation, and put the instantiation complexity on the client. Even if we use a factory method for every single family of classes this will still require several factory methods, unrelated to each other. Thereby managing them to make combination offerings (rooms) will be code cluttering.
We will use abstract factory when:
– A system is using families of related or dependent objects without any knowledge of their concrete types.
– The client does not need to know the instantiation details of subclasses.
– The client does not need to use subclasses in a concrete way.
Components
There are four components of this pattern.
– Abstract Factory
The abstraction client interacts with, to create door and window combinations. This is the core factory that provide interfaces for individual factories to implement.
– Concrete Factories
These are the concrete factories (CombinationFactoryA, CobinationFactoryB) that create concrete products (doors and windows).
– Abstract Products
These are the abstract products that will be visible to the client (AbstractDoor & AbstractWindow).
– Concrete Products
The concrete implementations of products offered. WoodenDoor, WoodenWindow etc.
Sample code
Using the above example, our implementation would be:
public interface Door
{
double GetPrice();
}
class WoodenDoor : Door
{
public double GetPrice()
{
//return price;
}
}
class GlassDoor : Door
{
public double GetPrice()
{
//return price
}
}
public interface Window
{
double GetPrice();
}
class WoodenWindow : Window
{
public double GetPrice()
{
//return price
}
}
class GlassWindow : Window
{
public double GetPrice()
{
//return price
}
}
The concrete classes and factories should ideally have protected or private constructors and should have appropriate access modifiers. e.g.
protected WoodenWindow()
{
}
The factories would be like:
public interface AbstractFactory
{
Door GetDoor();
Window GetWindow();
}
class CombinationA : AbstractFactory
{
public Door GetDoor()
{
return new WoodenDoor();
}
public Window GetWindow()
{
return new WoodenWindow();
}
}
class CombinationB : AbstractFactory
{
public Door GetDoor()
{
return new GlassDoor();
}
public Window GetWindow()
{
return new GlassWindow();
}
}
And the client:
public class Room
{
Door _door;
Window _window;
public Room(AbstractFactory factory)
{
_door = factory.GetDoor();
_window = factory.GetWindow();
}
public double GetPrice()
{
return this._door.GetPrice() + this._window.GetPrice();
}
}
…
AbstractFactory woodFactory = new CombinationA();
Room room1 = new Room(woodFactory);
Console.Write(room1.GetPrice());
AbstractFactory glassFactory = new CombinationB();
Room room2 = new Room(glassFactory);
Console.Write(room2.GetPrice());
The above showcases how abstract factory could be utilised to instantiate and use related or dependent families of classes via their respective abstractions without having to know or understand the corresponding concrete classes.
The Room class only knows about Door and Window abstractions and let the configuration/ client code input dictate which combination to use, at runtime.
Sometimes abstract factory also uses Factory Method or Static Factory Method for factory configurations:
public static class FactoryMaker
{
public static AbstractFactory GetFactory(string type) //some configuration
{
//configuration switches
if (type == “wood”)
return new CombinationA();
else if (type == “glass”)
return new CombinationB();
else //default or fault config
return null;
}
}
Which changes the client:
AbstractFactory factory = FactoryMaker.GetFactory(“wood”);//configurations or inputs
Room room1 = new Room(factory);
As can be seen, polymorphic behaviours are the core of these factories as well as the usage of related families of classes.
Advantages
Creational patterns, particularly Factory can work along with other creational patterns; Abstract factory
– Isolation of the creation mechanics from its usage for related families of classes.
– Adding new products/ concrete types does not affect the client code rather the configuration/ factory code.
– Provide way for the client to work with abstractions instead of concrete types. This gives flexibility to the client code to the related use cases.
– Usage of abstractions reduce dependencies across components and increases maintainability.
– Design often starts with Factory method and evolves towards Abstract factory (or other creational patterns) as the families of classes expends and their relationships develops.
Drawbacks
Abstract factory does introduce some disadvantages in the system.
– It has fairly complex implementation and as the families of classes grows, so does the complexity.
– Relying heavily on polymorphism does require expertise for debugging and testing.
– It introduces factory classes, which can be seen as added workload without having any direct purpose except for instantiation of other classes, particularly in bigger systems.
– Factory structures are tightly coupled with the relationships of the families of classes. This introduces maintainability issues and rigid design.
For example, adding a new type of window or door in the above example would not be as easy. Adding another family of classes, like Carpet and its sub types would be even more complex, but this does not affect the client code.
Conclusion
Abstract factory is a widely used creational pattern, particularly because of its ability to handle the instantiation mechanism of several families of related classes. This is helpful in real-world solutions where entities are often interrelated and work in a blend in a variety of use cases.
Abstract factory ensures the simplification of design targeting business processes by eliminating the concrete types and replacing them with abstractions while maintaining their encapsulation and removing the added complexity of object creation. This also reduces a lot of duplicate code at the client side making business processes testable, robust and independent of the underline concrete types.
In the third part of creational patterns, we will discuss a pattern slightly similar to Abstract factory, the Builder. Sometimes the two could be competitors in design decisions but differs in real-world applications.
Further reading
http://www.dofactory.com/net/abstract-factory-design-pattern
http://www.oodesign.com/abstract-factory-pattern.html
http://www.oodesign.com/abstract-factory-pattern.html