account_circle Toanph
date_range 15/06/2017
Factory Method
Purpose
The good point over the SimpleFactory is you can subclass it to implement different ways to create objects
For simple case, this abstract class could be just an interface
This pattern is a “real” Design Pattern because it achieves the “Dependency Inversion Principle” a.k.a the “D” in S.O.L.I.D principles.
It means the FactoryMethod class depends on abstractions, not concrete classes. This is the real trick compared to SimpleFactory or StaticFactory.
UML Diagram

Code
FactoryMethod.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
abstract class FactoryMethod
{
const CHEAP = 'cheap';
const FAST = 'fast';
abstract protected function createVehicle(string $type): VehicleInterface;
public function create(string $type): VehicleInterface
{
$obj = $this->createVehicle($type);
$obj->setColor('black');
return $obj;
}
}
|
ItalianFactory.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
class ItalianFactory extends FactoryMethod
{
protected function createVehicle(string $type): VehicleInterface
{
switch ($type) {
case parent::CHEAP:
return new Bicycle();
case parent::FAST:
return new CarFerrari();
default:
throw new \InvalidArgumentException("$type is not a valid vehicle");
}
}
}
|
GermanFactory.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
class GermanFactory extends FactoryMethod
{
protected function createVehicle(string $type): VehicleInterface
{
switch ($type) {
case parent::CHEAP:
return new Bicycle();
case parent::FAST:
$carMercedes = new CarMercedes();
// we can specialize the way we want some concrete Vehicle since we know the class
$carMercedes->addAMGTuning();
return $carMercedes;
default:
throw new \InvalidArgumentException("$type is not a valid vehicle");
}
}
}
|
VehicleInterface.php
| <?php
namespace DesignPatterns\Creational\FactoryMethod;
interface VehicleInterface
{
public function setColor(string $rgb);
}
|
CarMercedes.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
class CarMercedes implements VehicleInterface
{
/**
* @var string
*/
private $color;
public function setColor(string $rgb)
{
$this->color = $rgb;
}
public function addAMGTuning()
{
// do additional tuning here
}
}
|
CarFerrari.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
class CarFerrari implements VehicleInterface
{
/**
* @var string
*/
private $color;
public function setColor(string $rgb)
{
$this->color = $rgb;
}
}
|
Bicycle.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
class Bicycle implements VehicleInterface
{
/**
* @var string
*/
private $color;
public function setColor(string $rgb)
{
$this->color = $rgb;
}
}
|
Test
Tests/FactoryMethodTest.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 | <?php
namespace DesignPatterns\Creational\FactoryMethod\Tests;
use DesignPatterns\Creational\FactoryMethod\Bicycle;
use DesignPatterns\Creational\FactoryMethod\CarFerrari;
use DesignPatterns\Creational\FactoryMethod\CarMercedes;
use DesignPatterns\Creational\FactoryMethod\FactoryMethod;
use DesignPatterns\Creational\FactoryMethod\GermanFactory;
use DesignPatterns\Creational\FactoryMethod\ItalianFactory;
use PHPUnit\Framework\TestCase;
class FactoryMethodTest extends TestCase
{
public function testCanCreateCheapVehicleInGermany()
{
$factory = new GermanFactory();
$result = $factory->create(FactoryMethod::CHEAP);
$this->assertInstanceOf(Bicycle::class, $result);
}
public function testCanCreateFastVehicleInGermany()
{
$factory = new GermanFactory();
$result = $factory->create(FactoryMethod::FAST);
$this->assertInstanceOf(CarMercedes::class, $result);
}
public function testCanCreateCheapVehicleInItaly()
{
$factory = new ItalianFactory();
$result = $factory->create(FactoryMethod::CHEAP);
$this->assertInstanceOf(Bicycle::class, $result);
}
public function testCanCreateFastVehicleInItaly()
{
$factory = new ItalianFactory();
$result = $factory->create(FactoryMethod::FAST);
$this->assertInstanceOf(CarFerrari::class, $result);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage spaceship is not a valid vehicle
*/
public function testUnknownType()
{
(new ItalianFactory())->create('spaceship');
}
}
|