I know that there are many similar questions, but I don't understand most of those questions because I'm not sure if I know what a factory method pattern is.
So, after reading many examples over the web, I came up with the following simple classes.
Am I doing it correctly? If so...any improvements I can add?
abstract class Driveable
{
abstract public function start();
abstract public function stop();
}
class CoupeDriveable extends Driveable
{
public function start()
{
}
public function stop()
{
}
}
class MotorcycleDriveable extends Driveable
{
public function start()
{
}
public function stop()
{
}
}
class SedanDriveable extends Driveable
{
public function start()
{
}
public function stop()
{
}
}
class DriveableFactory
{
static public function create($numberOfPeople){
if( $numberOfPeople == 1 )
{
return new MotorcycleDriveable;
}
elseif( $numberOfPeople == 2 )
{
return new CoupleDriveable;
}
elseif( $numberOfPeople >= 3 && $numberOfPeople < 4)
{
return SedanDriveable;
}
}
}
class App
{
static public function getDriveableMachine($numberOfPeople)
{
return DriveableFactory::create($numberOfPeople);
}
}
$DriveableMachine = App::getDriveableMachine(2);
$DriveableMachine->start();
Update: according to palacsint and serghei's valueable advices, I've updated my code.
abstract class DriveableFactory
{
static public function create($numberOfPeople);
}
class CarDriveableFactory extends DriveableFactory
{
static public function create($numberOfPeople){
$products = array
(
1=>"MotorcycleDriveable",
2=>"CoupeDriveable",
3=>"SedanDriveable",
4=>"SedanDriveable"
);
if( isset( $products[$numberOfPeople] ) )
{
return new $products[$numberOfPeople];
}
else
{
throw new Exception("unable to find a suitable drivable car");
}
}
}
3 Answers 3
The essence of the Factory method Pattern is to "Define an interface for creating an object, but let the subclasses decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses."
If you insist on the above GoF definition there are two issues.
First, you should create a DriveableFactory
interface and rename your DriveableFactory
to (for example) CarDriveableFactory
.
abstract class DriveableFactory
{
static public function create($numberOfPeople);
}
class CarDriveableFactory extends DriveableFactory
{
static public function create($numberOfPeople) { ... }
}
But your code is fine, if you don't need (don't have a reason) the abstract DriveableFactory
interface do NOT add it to the code.
The second issue is that the create
method should not be static
. If it's static
subclasses cannot override the create
method.
Finally, the App
class looks unnecessary. So, I'd write something like this:
DriveableFactory factory = new CarDriveableFactory();
$DriveableMachine = factory->getDriveableMachine(2);
$DriveableMachine->start();
Some small improvements:
3.5
is an allowed value? And 3.1415
? If not consider changing
else if( $numberOfPeople >= 3 && $numberOfPeople < 4)
to
else if($numberOfPeople == 3 || $numberOfPeople == 4)
In the last line of the create()
method I would throw an IllegalArgumentException
(or a similar one in PHP) with the message "invalid value: " . $numberOfPeople
.
-
2\$\begingroup\$ Isn't this the Abstract Factory Pattern, and not the factory method pattern? \$\endgroup\$simao– simao2011年11月07日 17:17:22 +00:00Commented Nov 7, 2011 at 17:17
-
\$\begingroup\$ @simao: you are correct, the moment palacsint extended a factory to anther factory, this became an example of Abstract Factory Pattern. Factory Method would have created classes that inherits from a factory that has static methods to return one type of subclass. \$\endgroup\$Forethinker– Forethinker2013年09月09日 04:43:28 +00:00Commented Sep 9, 2013 at 4:43
I think is good implementation but you can reduce the multiple if else conditions I don't know the PHP I can write you as I implement in C# code in C# there is specific type Dictionary that represent key value pair collection and if in PHP exist some similar you can use it look at the foolwing code
private Driveable Create(int numberOfPeople)
{
Dictionary<int, Driveable> registerDriveable = new Dictionary<int, Driveable>();
registerDriveable.Add(1, new MotorcycleDriveable());
registerDriveable.Add(2, new CoupeDriveable());
registerDriveable.Add(3, new SedanDriveable());
registerDriveable.Add(4, new SedanDriveable());
// and then find in dictionary by key
//this code return the new CoupeDriveable()
Driveable driveable = registerDriveable[numberOfPeople];
return driveable;
}
benefit of this solutions is when you will add anther type of Driveable you don't need to add additional if else only one line
registerDriveable.Add(10, new BusDriveable());
-
1\$\begingroup\$ +1, it's also a good point, but it's worth notice that if creating the
Driveable
objects is slow theelse if
could be better. \$\endgroup\$palacsint– palacsint2011年11月02日 11:14:42 +00:00Commented Nov 2, 2011 at 11:14
As simao pointed out in a comment, this is an example of the Factory Pattern--not the Factory Method Pattern. In fact, you usually subclass the factory to determine the class to instantiate rather than making that determination based on a parameter. Perhaps this example mixes in the Builder Pattern or another whose name I'm forgetting at the moment.
The factory pattern defines an interface for creating instances where the concrete implementations define how to create those instances and what dependencies they require. Interfaces are used here because the factory's create
method is called by other classes.
While related, the factory method pattern differs in that an abstract class defines a protected create
method that is called by the concrete subclasses--never externally. You can always replace the method-based pattern with an external factory to increase flexibility.
Here's a simple example of using the factory method pattern:
Abstract Factory
abstract class Race
{
private $racers = array();
public function __construct($numRacers) {
for ($i = 0; $i < $numRacers; $i++) {
$this->racers[] = $this->createRacer($i);
}
}
protected abstract function createRacer($racerNum);
public function startRace() { ... use $this->racers ... }
}
Concrete Factory Implementations
class MotorcycleRace extends Race
{
protected function createRacer($racerNum) {
return new MotorcycleRacer($racerNum);
}
}
class DragsterRace extends Race
{
protected function createRacer($racerNum) {
return new DragsterRacer($racerNum);
}
}
class FormulaOneRace extends Race
{
protected function createRacer($racerNum) {
return new FormulaOneRacer($racerNum);
}
}
create
function returns a string instead of an object. Was this for the sake of simplication? You got the Factory Method right though. \$\endgroup\$