| Type | Creational |
| Purpose | Provides an interface that is used to create families of related objects without specifying their concrete classes. |
Description
The Abstract Factory pattern provides an interface for creating families of related objects without specifying their concrete classes. These products are designed to be used together, and the client accesses them only through factory and product abstractions rather than concrete class names.
This creates a layer of separation between the client and the concrete factories and products it uses. Because the client depends only on the AbstractFactory and abstract product interfaces, one concrete factory can be substituted for another without changing the client's overall structure.
Each concrete factory is responsible for creating a consistent family of products, while the client remains independent of how those products are created, composed, and implemented. This can also make it possible to vary whole product families at runtime.
Abstract Factory is primarily concerned with which products are made together.
Overview
| AbstractFactory | An interface with create operations for each of the abstract products. |
| ConcreteFactory1, ConcreteFactory2 | Implementations of the AbstractFactory interface that create specific families of products. |
| AbstractProductA, AbstractProductB | Interfaces for kinds of products with their own operations. |
| ProductA1, ProductA2, ProductB1, ProductB2 | Classes that implement the AbstractProduct interface and define product objects to be created by the corresponding factories. |
| Client | A class that accesses only the AbstractFactory and AbstractProduct interfaces. |
Implementation
// Abstract Factory Pattern
public interface IAbstractBreakfastFactory
{
public IAbstractFood CreateFood();
public IAbstractBeverage CreateBeverage();
}
// Abstract Products A & B
public interface IAbstractFood
{
public bool IsSpicy { get; set; }
public string Consume();
}
public interface IAbstractBeverage
{
public bool IsHot { get; set; }
public string Consume();
}
// Concrete Factories 1 & 2
public class EnglishBreakfastFactory : IAbstractBreakfastFactory
{
public IAbstractFood CreateFood() => new SausagesAndEggs();
public IAbstractBeverage CreateBeverage() => new YorkshireTea();
}
public class IndianBreakfastFactory : IAbstractBreakfastFactory
{
public IAbstractFood CreateFood() => new RottiPrata();
public IAbstractBeverage CreateBeverage() => new MasalaChai();
}
// Concrete Products A1 & A2
public class SausagesAndEggs : IAbstractFood
{
public bool IsSpicy { get; set; } = false;
public string Consume() => "yummy...sausages and eggs!";
}
public class RottiPrata : IAbstractFood
{
public bool IsSpicy { get; set; } = true;
public string Consume() => "delicious...spicy...rotti prata!";
}
// Concrete Products B1 & B2
public class YorkshireTea : IAbstractBeverage
{
public bool IsHot { get; set; } = true;
public string Consume() => "hmmm...delicious!";
}
public class MasalaChai : IAbstractBeverage
{
public bool IsHot { get; set; } = true;
public string Consume() => "wow!...spicy! delicious!";
}
// Client
public class FoodCenter
{
// returns a breakfast stall (factory) which can provide breakfast
public IAbstractBreakfastFactory VisitBreakfastStall(string type)
=> type switch
{
"English" => new EnglishBreakfastFactory(),
"Indian" => new IndianBreakfastFactory(),
_ => throw new ArgumentException("no such stall in the food center.");
};
}
// Demonstration
public static class AbstractFactoryDemo
{
public static void Execute()
{
var center = new FoodCenter();
var stall = center.VisitBreakfastStall("English");
var food = stall.CreateFood();
var drink = stall.CreateBeverage();
Console.WriteLine(food.Consume());
Console.WriteLine(drink.Consume());
stall = center.VisitBreakfastStall("Indian");
food = stall.CreateFood();
drink = stall.CreateBeverage();
Console.WriteLine(food.Consume());
Console.WriteLine(drink.Consume());
}
}
Summary
A trade-off of Abstract Factory is that while adding new product families is straightforward, adding a new kind of product requires changing the abstract factory and each concrete factory. The pattern therefore works particularly well when the set of products in a family is relatively stable.
For more information, please see here.