The Abstract Factory Pattern is a creational pattern in which interfaces are defined for creating families of related objects without specifying their actual implementations.
When using this pattern, you create factories which return many kinds of related objects. This pattern enables larger architectures such as Dependency Injection.
NOTE: This post is part of a series of posts demonstrating software design patterns using C# and .NET. The patterns are taken from the book Design Patterns by the Gang of Four. Check out the other posts in this series!
The Rundown
- Type: Creational
- Useful?: 5/5 (Extremely)
- Good For: Creating objects in different related families without relying on concrete implementations.
- Example Code: On GitHub
The Participants
- The AbstractFactory declares an interface for operations which will create AbstractProduct objects.
- The ConcreteFactory objects implement the operations defined by the AbstractFactory.
- The AbstractProduct declares an interface for a type of product.
- The Products define a product object that will be created by the corresponding ConcreteFactory.
- The Client uses the AbstractFactory and AbstractProduct interfaces.
A Delicious Example
When we modeled the Factory Method pattern, we did so using sandwiches. The thing about sandwiches is that they no matter what they are made of (turkey, roast beef, veggies, peanut butter and jelly) they're still sandwiches, e.g. something edible between two slices of bread. In that example, sandwiches could be considered a family of related objects. But what if wanted to model several families of objects, not just one?
For this demo, let's go more general and model entire sets of recipes.
Let's say we want to model two kinds of recipes: a Sandwich and a Dessert. Further, let's make the assumption that adults and kids don't eat the same things, and so we want one of each kind of recipe for adults and children.
To demo this, let's make some abstract classes representing the generic kinds of recipes (these are our AbstractProduct participants):
/// <summary>
/// An abstract object.
/// </summary>
abstract class Sandwich { }
/// <summary>
/// An abstract object.
/// </summary>
abstract class Dessert { }
Next, we need an abstract class that will return a Sandwich
and a Dessert
(this is the AbstractFactory participant):
/// <summary>
/// The AbstractFactory class, which defines methods for creating abstract objects.
/// </summary>
abstract class RecipeFactory
{
public abstract Sandwich CreateSandwich();
public abstract Dessert CreateDessert();
}
Now we can start implementing the actual objects. First let's consider the adult menu (these next classes are ConcreteProduct objects):
/// <summary>
/// A ConcreteProduct
/// </summary>
class BLT : Sandwich { }
/// <summary>
/// A ConcreteProduct
/// </summary>
class CremeBrulee : Dessert { }
We also need a ConcreteFactory which implements the AbstractFactory and returns the adult recipes:
/// <summary>
/// A ConcreteFactory which creates concrete objects by implementing the abstract factory's methods.
/// </summary>
class AdultCuisineFactory : RecipeFactory
{
public override Sandwich CreateSandwich()
{
return new BLT();
}
public override Dessert CreateDessert()
{
return new CremeBrulee();
}
}
Now let's define the Child's recipes. Here are the ConcreteProduct classes and ConcreteFactory for said recipes:
/// <summary>
/// A concrete object
/// </summary>
class GrilledCheese : Sandwich { }
/// <summary>
/// A concrete object
/// </summary>
class IceCreamSundae : Dessert { }
/// <summary>
/// A concrete factory which creates concrete objects by implementing the abstract factory's methods.
/// </summary>
class KidCuisineFactory : RecipeFactory
{
public override Sandwich CreateSandwich()
{
return new GrilledCheese();
}
public override Dessert CreateDessert()
{
return new IceCreamSundae();
}
}
How do we use this? First, let's ask the user if they are an adult or a child, then display the corresponding menu items.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Who are you? (A)dult or (C)hild?");
char input = Console.ReadKey().KeyChar;
RecipeFactory factory;
switch(input)
{
case 'A':
factory = new AdultCuisineFactory();
break;
case 'C':
factory = new KidCuisineFactory();
break;
default:
throw new NotImplementedException();
}
var sandwich = factory.CreateSandwich();
var dessert = factory.CreateDessert();
Console.WriteLine("\nSandwich: " + sandwich.GetType().Name);
Console.WriteLine("Dessert: " + dessert.GetType().Name);
Console.ReadKey();
}
}
When we run the app, the output looks something like this:
Will I Ever Use This Pattern?
Unquestionably. Abstract Factory is an extremely common pattern, and as mentioned earlier it enables architectures such as Dependency Injection. That said, it's also one of the patterns that's prone to overuse: it's easy to start using Abstract Factories anytime you need to create objects. Be aware of when you decide to use this pattern, and make sure you actually need it.
Summary
Abstract Factory allows us to generically define families of related objects, leaving the actual concretions for those objects to be implemented as needed.
Happy Coding!