The factory design pattern is a creational pattern that decouples object construction from the class that uses the object. Rather than instantiating concrete types with new scattered through the code, a factory method centralises construction — making it easy to swap, extend, or test without touching the consuming code.
When to apply it: the target object needs to be extended to sub-classes; the base class should not be aware of its concrete sub-classes; or the product implementation is expected to change over time.
Without Factory Pattern
A classic OOP approach defines the model hierarchy and a factory class that switches on the type enum to return the correct instance. Adding a new model means touching the switch statement here — and in every similar switch throughout the codebase:
// Base car class — all Audi models inherit this
public abstract class AudiCar
{
public AudiModels CarType { get; }
public AudiCar(AudiModels carType) => CarType = carType;
public override string ToString() => CarType.ToString();
}
public enum AudiModels { AudiEPhaeton, AudiA, AudiC }
public class AudiModelA : AudiCar { public AudiModelA() : base(AudiModels.AudiA) {} }
public class AudiModelC : AudiCar { public AudiModelC() : base(AudiModels.AudiC) {} }
public class AudiModelEPhaeton: AudiCar { public AudiModelEPhaeton(): base(AudiModels.AudiEPhaeton) {} }// Classic factory — every new model requires editing this switch statement
// and every other switch statement like it scattered across the codebase
public class AudiFactory
{
public AudiCar Buy(AudiModels audiCarType)
{
switch (audiCarType)
{
case AudiModels.AudiA: return new AudiModelA();
case AudiModels.AudiC: return new AudiModelC();
case AudiModels.AudiEPhaeton: return new AudiModelEPhaeton();
default: return null;
}
}
}Factory Design Pattern
Introduce an IAudiSpecs interface that mandates a factory method. Each model gets its own specs class that implements the method. The factory only knows the interface — adding a model is a new class, not an edit:
// The base class stays the same.
// Add an interface that forces each model to supply its own factory method.
public interface IAudiSpecs
{
AudiCar CreateCar();
}
// Each model's specs class implements the factory method in isolation —
// adding a new model means adding a new class, not editing existing ones.
public class AudiASpecs : IAudiSpecs
{
public AudiCar CreateCar() => new AudiA();
}
public class AudiEPhaetonSpecs : IAudiSpecs
{
public AudiCar CreateCar() => new AudiModelEPhaeton();
}
// The factory interface works on IAudiSpecs, never on concrete model types.
public interface IAudiFactory
{
string Location { get; }
AudiCar CreateCar(IAudiSpecs audiSpecs);
}
// Location-specific factories — each delegates creation to the specs object.
public class AudiNewYorkFactory : IAudiFactory
{
public string Location => "NewYork";
public AudiCar CreateCar(IAudiSpecs audiSpecs) => audiSpecs.CreateCar();
}
public class AudiSingaporeFactory : IAudiFactory
{
public string Location => "Singapore";
public AudiCar CreateCar(IAudiSpecs audiSpecs) => audiSpecs.CreateCar();
}
// Usage: the caller selects factory + specs; no switch statements needed.
AudiNewYorkFactory factory = new AudiNewYorkFactory();
AudiCar car = factory.CreateCar(new AudiASpecs());Abstract Factory
An abstract builder combines a factory with a specific model, giving callers a single entry point. The trade-off is one class per factory/model combination, but each class is small and change is non-invasive — adding a new combination requires only a new class:
// Abstract builder couples a factory to a specific model.
// New combinations require only a new class — nothing else changes.
public abstract class AudiAbstractBuilder
{
protected IAudiFactory AudiFactory;
public AudiAbstractBuilder(IAudiFactory factory) => AudiFactory = factory;
public abstract AudiCar OrderCar();
}
// NY factory producing only model E
public class AudiEPhaetonNYBuilder : AudiAbstractBuilder
{
public AudiEPhaetonNYBuilder() : base(new AudiNewYorkFactory()) {}
public override AudiCar OrderCar() => AudiFactory.CreateCar(new AudiEPhaetonSpecs());
}
// Singapore factory producing model E
public class AudiEPhaetonSBuilder : AudiAbstractBuilder
{
public AudiEPhaetonSBuilder() : base(new AudiSingaporeFactory()) {}
public override AudiCar OrderCar() => AudiFactory.CreateCar(new AudiEPhaetonSpecs());
}
// Usage
AudiAbstractBuilder builder = new AudiEPhaetonNYBuilder();
AudiCar audiCar = builder.OrderCar();