The Abstract Factory (The Factory Pattern – part 3)

1. Definition

It provides an interface for creating families of related or dependent objects without specifying their concrete classes.

2. Case study

2.1 The problem

You’re creating a car manufacturer simulator, where you can simulate the process of manufacturing cars.

As you may know, petrol cars and diesel cars need different parts in their fuel systems, since they burn fuel (petrol or diesel) in a different way.

In this car manufacturer simulator, you’ll need to develop two factories, one is for petrol and the other for diesel cars.

(You may argue that electric cars and hybrid cars are trendier, but please forget about them for now)

Here is a snippet of code for the factories.

abstract class CarFactory
{
	// Create properties and functions which will be
	// used in both factories

	abstract protected function createCar($type);
	abstract protected function assembleCar($type);
}

class PetrolCarFactory extends CarFactory
{
    public function createCar($type)
    {
        if ($type === "sedan") {
            return $car = new Sedan();
        }else if ($type === "suv") {
            return $car = new Suv();
        }else if ($type === "van") {
            return $car = new Van();
        }else if ($type === "compact") {
            return $car = new Compact();
        }
        //other types of cars, such as pickup trucks
    }

    public function assembleCar($car)
    {
    	$car = $this->createCar($type);
    	return $car->assemble();
    }
}

class DieselCarFactory extends CarFactory
{
    //Implement createCar($type) and assembleCar($car)
}

(By the way, we’re seeing Factory method here)

What about the cars?

abstract class Car
{
    protected $fuelPump;
    protected $fuelInjector;
    //The properties of this class represent car parts. 
    //Of course we need many other parts here, such as
    //$brakes, $doors, $fuelTank etc.

    abstract public function assemble();
}

class Sedan extends Car
{
    public function assemble(){}
    //This function will assemble a car
}
//Suv, Van and other cars would have similar functions

Again, depending on the fuel, the same cars will need different parts in their fuel system (fuel pump, fuel injector, fuel tank etc…).

The assemble() function will assemble a car object, but we need to provide the object with the right parts before calling the function.

So, how many parts do we need?

NOTE: You don’t need to read every line in the following code. Just see that the combinations of fuel types and car types result in a large number of parts

//Here we have the fuel pump interface
interface FuelPump{}

class PetrolSedanPump implements FuelPump{}
class PetrolSuvPump implements FuelPump{}
class PetrolVanPump implements FuelPump{}
//we need more pumps for other types of petrol cars.

//Likewise, we need fuel pumps for diesel cars!
class DieselSedanPump implements FuelPump{}
class DieselSuvPump implements FuelPump{}
class DieselVanPump implements FuelPump{}
//and so on.

//Here we have the fuel injector interface
interface FuelInjector{}

//These are used for petrol cars
class PetrolSedanInjector implements FuelInjector{}
class PetrolSuvInjector implements FuelInjector{}
class PetrolVanInjector implements FuelInjector{}
//Again, we need more parts for other petrol cars.

//These are used for diesel cars.
class DieselSedanInjector implements FuelInjector{}
class DieselSuvInjector implements FuelInjector{}
class DieselVanInjector implements FuelInjector{}
//continues forever...

Can you imagine the overwhelming number of car parts that this car manufacturer deals with?

Here we need a framework where we can provide an object (a car) with right objects (pumps, injectors etc) among so many options without messing around the client (petrol and diesel car factories).

One way to accomplish this task is to create the Abstract factory.

2.2 The solution

1. Build an interface/abstract class for creating the parts.

interface CarPartsFactory
{
    public function getFuelPump($type);
    public function getFuelInjector($type);
    //and so on.
}

This interface (or abstract class) is the base of our Abstract factory, responsible for creating all the car parts required for the petrol and diesel car factories.

In case you don’t remember the definition of Abstract factory, let’s recap it here…

The Abstract factory gives us an interface for creating a family of instances

(i.e. car parts, in this case).

2.Build each parts factory (i.e. abstract factories)

class PetrolCarPartsFactory implements CarPartsFactory
{
    public function getFuelPump($type)
    {
        if ($type === "sedan") {
            return new PetrolSedanPump();
        } else if ($type === "suv") {
            return new PetrolSuvPump();
        }
        //return other fuel pumps
        //based on $type.
    }

    public function getFuelInjector($type)
    {
        if ($type === "sedan") {
            return new PetrolSedanInjector();
        } else if ($type === "suv") {
            return new PetrolSuvInjector();
        }
        //likewise.
    }

    //there will be other functions, such as
    //getTank() for getting a tank for petrol cars.
}
//you'll build DieselCarPartsFactory in a similar way

I put an argument ($type) and if statements in the functions of both parts factories. However, the argument and if statements themselves are not to do with the Abstract factory pattern. I did so just to simulate the car manufacturer.

(If you write something like “with the Abstract factory pattern, concrete factories’ functions have to take an argument” in the exam of your software engineering class, you may lose a mark.)

Now we’ve just created the Abstract factory!

Let’s see how this Abstract factory can be applied for the imaginary car manufacturer.

3. Add the CarPartsFactory as a property in the CarFactory class.

abstract class CarFactory
{
    protected $carPartsFactory;
}

4. Update PetrolCarFactory and DieselCarFactory, so that they’ll have the appropriate parts factories

class PetrolCarFactory extends CarFactory
{
    public function __construct(PetrolCarPartsFactory $petrolCarPartsFactory)
    {
        $this->carPartsFactory = $petrolCarPartsFactory;
    }

    public function createCar($type)
    {
        if ($type === "sedan") {
            return new Sedan($this->carPartsFactory);
        }else if ($type === "suv") {
            return new Suv($this->carPartsFactory);
        }else if ($type === "van") {
            return new Van($this->carPartsFactory);
        }else if ($type === "compact") {
            return new Compact($this->carPartsFactory);
        }
    }

    public function assembleCar($type)
    {
        $car = $this->createCar($type);
        return $car->assemble($type);
    }
}

5. Update abstract class Car and its subclasses

abstract class Car
{
    protected $carPartsFactory;
    //added!

    protected $fuelPump;
    protected $fuelInjector;
    //many other parts here

    abstract public function assemble($type);
}


class Sedan extends Car
{
    public function __construct(CarPartsFactory $carPartsFactory)
    {
        $this->carPartsFactory = $carPartsFactory;
    }

    public function assemble($type){
	$this->$fuelPump     = $this->carPartsFactory->getFuelPump($type);
	$this->$fuelInjector = $this->carPartsFactory->getFuelInjector($type);
	//and so on.
    }    
}
//Suv, Van and other cars would be very similar

2.3 The outcome

Now we’re able to create cars with the right car parts for different factories.

With the Abstract factory, clients (PetrolCarFactory and DieselCarFactory) can be decoupled from the object creation logic, which will allow us to implement various clients.

3. The structure

4.When to use this pattern

When you are creating various objects (fuel pump, fuel injector, etc) of the same domain (properties of abstract Car class) without the client (car factory) knowing the details of those objects.

Leave a Reply

Your email address will not be published. Required fields are marked *