In the previous article, we went through the Simple Factory Pattern. Now in this article we will examine the Factory Method Design pattern. In the case of Simple Factory, it provides an interface to create objects, while the Factory method does the same thing but in addition it allows a subclass to make a decision on which class to instantiate.
For explaining the Simple Factory Method pattern, I used the example of creating an object of a car with various car types, which are Sedan, SUV, etc. I will continue the same example to explain the Factory Method pattern so we will have a better idea and continuity.
The Problem
In a simple factory, we were creating an object of the car class but actually it was creating an object of the car class based on the car type we have passed. This was a good step to start with creating objects for a car with different types.
Earlier our company was limited to selling a car in the USA only, and it has one production center in the USA only. But as time goes on, the company expands and it decides to sell cars in the UK and build a new production center in the UK too.
In both production centers, our old code to build cars works fine, but then to improve customer satisfaction, the company decides on some changes in the car model, but for UK cars only. At this stage, our code to build an object of a car will fail because we need some extra features (attributes) for one specific type of cars only.
We have two options in this case: either modify the current code (multiple if else conditions) to get the desired object of car, or restructure our classes in such a way that does not require dirty if else conditions in your class and in future if you want to add a few more features but for limited types of class only.
The first approach no one would like to go with because it's a kind of patchwork you are doing in your code and not the real implementation. In the next section, we will see how we can implement the second approach by using the Factory Method design pattern.
The Solution
This design pattern falls under the structural pattern category, so it is more concerned with how you structure your classes. So let's get to the solution of our problem by structuring our classes in the proper way.
In a simple factory, we did not use a structure because we needed only one class which was responsible for creating an object of the car. Based on the current problem, we need to create a separate factory class for both production centers, so we want to make sure all our factories are following the same process to create an object of a class. Hence, we will make one interface which both the US and UK factory will implement.
interface carFactory { public function buildCar($type); }
Now our interface for factories is ready, and it's time to create classes of our factories which are the US and UK car factory.
// Factory class to build US based center class USCarFactory implements carFactory { public function __construct() { } public function buildCar($type){ $car = null; switch($type) { case 'suv': $car = new USSuvFactory(); break; case 'sedan': $car = new USSedanFactory(); break; default: $car = new USSedanFactory(); break; } return $car; } } // Factory class to build UK based center class UKCarFactory implements carFactory { public function __construct() { } public function buildCar($type){ $car = null; switch($type) { case 'suv': $car = new UKSuvFactory(); break; case 'sedan': $car = new UKSedanFactory(); break; default: $car = new UKSedanFactory(); break; } return $car; } }
At this stage, we have our factory classes ready, and it's time now to create our concrete class. But as with the factory, we want our concrete classes to implement some required methods which are common to all concrete classes.
interface car { public function getLocation(); public function getType(); }
We have added very basic methods to our class to get the location and type of car. Now we will implement our concrete classes as below.
class USSuvFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'US'; $this->carType = 'SUV'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } } class USSedanFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'US'; $this->carType = 'Sedan'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } } class UKSuvFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'UK'; $this->carType = 'SUV'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } } class UKSedanFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'UK'; $this->carType = 'Sedan'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } }
We are done with the implementation of our concrete classes, which actually are an object of car based on location and type of car. Here what's left is to use this implementation to see how it solves our problem explained above.
// US Car Factory $USFactory = new USCarFactory(); // US based SUV model $USSuv = $USFactory->buildCar('suv'); echo $USSuv->getLocation().' based '. $USSuv->getType(); // US based Sedan model $USSedan = $USFactory->buildCar('sedan'); echo $USSedan->getLocation().' based '. $USSedan->getType(); // UK Car Factory $UKFactory = new UKCarFactory(); // UK based SUV model $UKSuv = $UKFactory->buildCar('suv'); echo $UKSuv->getLocation().' based '. $UKSuv->getType(); // UK based Sedan model $UKSedan = $UKFactory->buildCar('sedan'); echo $UKSedan->getLocation().' based '. $UKSedan->getType();
You can say that we have effectively created objects of all possible types of car with the location. Our classes are well structured now, so there won't be much trouble adding a new factory.
Adding a New Factory Class
Let's imagine a company has decided to start a new production center in Australia.
As our classes are well structured now, there won't be an issue adding a new class now to build a car object for the Australian production center.
// Factory class to build AUS based center class AUSCarFactory implements carFactory { public function __construct() { } public function buildCar($type){ $car = null; switch($type) { case 'suv': $car = new AUSSuvFactory(); break; case 'sedan': $car = new AUSSedanFactory(); break; default: $car = new AUSSedanFactory(); break; } return $car; } } // Concrete classes for AUS based center class AUSSuvFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'AUS'; $this->carType = 'SUV'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } } class AUSSedanFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'AUS'; $this->carType = 'Sedan'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } } // AUS Car Factory $AUSFactory = new AUSCarFactory(); // AUS based SUV model $AUSSuv = $AUSFactory->buildCar('suv'); echo $AUSSuv->getLocation().' based '. $AUSSuv->getType(); // AUS based Sedan model $AUSSedan = $AUSFactory->buildCar('sedan'); echo $AUSSedan->getLocation().' based '. $AUSSedan->getType();
Wrapping Up
So we have seen how we can structure our classes in such a way that they can be extended without making any changes to your base classes and client code. Feel free to add your inputs in the comment section below or tweet me at @XpertDevelopers.
Comments