Tuesday, November 29, 2011

The Abstract Factory Pattern

In our last tutorial we talked about the factory method pattern which is a useful way to separate out our object creation and delegate the creation specifics to creator subclasses.
Let's take a look at another possible Car class.
abstract class Car
{
    //A car is made up of parts like an engine and wheels.
    Engine _engine;
    Wheel[] _wheels = new Wheel[4];

    //methods...
}
Here we have a Car class complete with parts. The Engine and Wheel classes would also be abstract since their types could vary. We may want a Car with a HybridEngine or special PunctureProofWheels. Different Car subclasses like Toyota or Honda could have options for different Wheels or Engines and we want to encapsulate both the creation of Cars and its parts while keeping the whole thing flexible for extension.
This may seem daunting but there is a fairly simple pattern that can help called the Abstract Factory pattern.
In this pattern we'll make Factory interface for creating families of products like different Wheels and Engines.
So let's jump right in and make a CarPartFactory class.
interface CarPartFactory
{
    Engine CreateEngine();
    Wheel[] CreateWheels();
}
Any factory class that inherits from CarPartFactory will be responsible for which concrete parts we create. Let's make a couple to see how.
class StandardCarPartFactory : CarPartFactory
{
    public Engine CreateEngine()
    {
        return new CombustionEngine();
    }

    public Wheel[] CreateWheels()
    {
        Wheel[] wheels = new Wheel[4];
        for (int i = 0; i < wheels.Length; i++)
        {
            wheels[i] = new StandardCarWheel();
        }
        return wheels;
    }
}

class HybridCarPartFactory : CarPartFactory
{
    public Engine CreateEngine()
    {
        return new HybridEngine();
    }

    public Wheel[] CreateWheels()
    {
        Wheel[] wheels = new Wheel[4];
        for (int i = 0; i < wheels.Length; i++)
        {
            //Pucture proof wheels come standard with the hybrid.
            wheels[i] = new PunctureProofWheels();
        }
        return wheels;
    }
}
We need to make Engine and Wheel classes as well. In this example they are very simple and the ones I wrote just print out that they are being made and what type so I'll leave them out.
Each Car object with be passed a CarPartFactory in its construction that will set the chosen wheels and engine.
Let's rework our Car class a bit.
abstract class Car
{
    protected Engine _engine;
    protected Wheel[] _wheels = new Wheel[4];
       
    //This is where we'll call the factory methods to make its parts.
    public abstract void ConstructCar();

    public void Paint()
    {
        Console.WriteLine("Painting car...");
    }

    public void ApplyNewCarSmell()
    {
        Console.WriteLine("Applying new car smell...");
    }
}

class Toyota : Car
{
    //Each Car has a factory reference.
    CarPartFactory _factory;
        
    public Toyota(CarPartFactory factory)
    {
        _factory = factory;
        Console.WriteLine("New Toyota created.");
    }

    public override void ConstructCar()
    {
        //The factory creates the correct engine and wheels.
        _engine = _factory.CreateEngine();
        _wheels = _factory.CreateWheels();
    }
}

//The Honda class looks very similar.
That takes care of the Cars and the part creators. We still need our old CarLot method for creating cars and choosing the correct factory.
//This should look familiar.  It's the old Factory Method pattern!
abstract class CarLot
{
    public Car BuyCar(string type)
    {
        Car product = CreateCar(type);

        product.ConstructCar();
        product.Paint();
        product.ApplyNewCarSmell();

        return product;
    }
    //factory method...
    protected abstract Car CreateCar(string type);
}

class ToyotaCarLot : CarLot
{
    protected override Car CreateCar(string type)
    {
        CarPartFactory factory;
        //Except now the 'string type' parameter declares a factory for creating parts.
        if (type == "Hybrid")
        {
            factory = new HybridCarPartFactory();
        }
        else if (type == "Standard")
        {
            factory = new StandardCarPartFactory();
        }
        else
        {
            factory = new StandardCarPartFactory();
        }
        //And we get a Toyota...
        Car car = new Toyota(factory);
        return car;
    }
}

class HondaCarLot : CarLot
{
    protected override Car CreateCar(string type)
    {
        CarPartFactory factory;
        if (type == "Hybrid")
        {
            factory = new HybridCarPartFactory();
        }
        else if (type == "Standard")
        {
            factory = new StandardCarPartFactory();
        }
        else
        {
            factory = new StandardCarPartFactory();
        }
        Car product = new Honda(factory);
        return product;
    }
}
Both lots have a Standard and Hybrid options but the ToyotaCarLot return Toyotas and the HondaCarLot returns Hondas.
Let's test it out and buy some cars.
class Program
{
    static void Main(string[] args)
    {
        CarLot _hondaLot = new HondaCarLot();
        CarLot _toyotaLot = new ToyotaCarLot();
        Car myHybrid = _hondaLot.BuyCar("Hybrid");
        Car myStandard = _toyotaLot.BuyCar("Standard");
        Console.ReadLine();
    }
}
Running this should print out this result.

That's the Abstract Factory pattern! It encapsulates the creation of families of objects and allows for easy extension of any interface we'd like, cars or parts.

No comments :

Post a Comment