The Abstract Factory pattern helps objects create and interact with general types, leaving the actual concrete implementation of types and their members business logic to concrete types. This aids decoupling general behaviors from specific case behaviors
Goal
Often applications use types which behave similar. These types have general behaviors which lend themselves to be defined in a base type, leaving the unique behaviors to be defined in the child types.
The problem is however making applications which create specialist concrete types can itself be result in sections of code which are full of repetition.
The alternative, is to split general types and specialist types up, both the General Product types from Concrete Product types and General Factory types from Concrete Factory types.
Implementation
class Client {
AbstractProductA productA;
AbstractProductB productB;
AbstractFactory factory;
Client(AbstractFactory factory) {
this.factory = factory;
this.productA = factory.CreateProductA();
this.productB = factory.CreateProductB();
}
public string Process() {
return $"{productA.Value} + {productB.Value}";
}
}
public class AbstractProductA {
abstract string Value;
}
public class AbstractProductB {
abstract string Value;
}
public class ConcreteProductA1 {
public override string Value = "ConcreteProductA1";
}
public class ConcreteProductA2 {
public override string Value = "ConcreteProductA2";
}
public class ConcreteProductB1 {
public override string Value = "ConcreteProductB1";
}
public class ConcreteProductB2 {
public override string Value = "ConcreteProductB2";
}
public abstract class AbstractFactory {
abstract AbstractProductA CreateProductA();
abstract AbstractProductB CreateProductB();
}
public class ConcreteFactory1 : AbstractFactory {
public override AbstractFactoryA CreateProductA() {
return new ConcreteProductA1();
}
public override AbstractFactoryB CreateProductB() {
return new ConcreteProductB1();
}
}
public class ConcreteFactory2 : AbstractFactory {
public override AbstractFactoryA CreateProductA() {
return new ConcreteProductA2();
}
public override AbstractFactoryB CreateProductB() {
return new ConcreteProductB2();
}
}
Benefits:
- The core client code is ignorant of the specific details in either the concrete factory or concrete product types
- The specific details are encapsulated in bespoke types which are adhering to a common standard interface
Use
var client1 = new Client(new ConcreteFactory1());
var client2 = new Client(new ConcreteFactory2());