1.3. 팩토리 메서드 (Factory Method)¶
1.3.1. 사용 목적¶
단순팩토리(SimpleFactory)의 좋은점은 서브클래스가 객체를 여러 방법으로 구현할 수 있다는 것 입니다.
간단한 예로는, 이 추상 클래스는 인터페이스가 될 수 있다는 것입니다.
이 패턴은 “real” 디자인 패턴 입니다. 왜냐하면 S.O.L.I.D 원리에서 “D”에 해당하는, 일명 “역 의존성 원리(Dependency Inversion Principle)”를 달성해야 하기 때문입니다.
이 말은 FactoryMethod(코드에 언급된)클래스는 구체적인 클래스가 아닌, 추상화에 따른다는 것입니다. 이것은 단순팩토리나 정적팩토리에 비하면 진짜 기술입니다.
1.3.2. UML 다이어그램¶
1.3.3. 코드¶
코드는 또한 GitHub 에서 볼 수 있습니다.
FactoryMethod.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 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
/**
* class FactoryMethod
*/
abstract class FactoryMethod
{
const CHEAP = 1;
const FAST = 2;
/**
* The children of the class must implement this method
*
* Sometimes this method can be public to get "raw" object
*
* @param string $type a generic type
*
* @return VehicleInterface a new vehicle
*/
abstract protected function createVehicle($type);
/**
* Creates a new vehicle
*
* @param int $type
*
* @return VehicleInterface a new vehicle
*/
public function create($type)
{
$obj = $this->createVehicle($type);
$obj->setColor("#f00");
return $obj;
}
}
|
ItalianFactory.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 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
/**
* ItalianFactory is vehicle factory in Italy
*/
class ItalianFactory extends FactoryMethod
{
/**
* {@inheritdoc}
*/
protected function createVehicle($type)
{
switch ($type) {
case parent::CHEAP:
return new Bicycle();
break;
case parent::FAST:
return new Ferrari();
break;
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 23 24 25 26 27 28 29 30 31 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
/**
* GermanFactory is a vehicle factory in Germany
*/
class GermanFactory extends FactoryMethod
{
/**
* {@inheritdoc}
*/
protected function createVehicle($type)
{
switch ($type) {
case parent::CHEAP:
return new Bicycle();
break;
case parent::FAST:
$obj = new Porsche();
// we can specialize the way we want some concrete Vehicle since
// we know the class
$obj->addTuningAMG();
return $obj;
break;
default:
throw new \InvalidArgumentException("$type is not a valid vehicle");
}
}
}
|
VehicleInterface.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
/**
* VehicleInterface is a contract for a vehicle
*/
interface VehicleInterface
{
/**
* sets the color of the vehicle
*
* @param string $rgb
*/
public function setColor($rgb);
}
|
Porsche.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 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
/**
* Porsche is a german car
*/
class Porsche implements VehicleInterface
{
/**
* @var string
*/
protected $color;
/**
* @param string $rgb
*/
public function setColor($rgb)
{
$this->color = $rgb;
}
/**
* although tuning by AMG is only offered for Mercedes Cars,
* this is a valid coding example ...
*/
public function addTuningAMG()
{
}
}
|
Bicycle.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 | <?php
namespace DesignPatterns\Creational\FactoryMethod;
/**
* Bicycle is a bicycle
*/
class Bicycle implements VehicleInterface
{
/**
* @var string
*/
protected $color;
/**
* sets the color of the bicycle
*
* @param string $rgb
*/
public function setColor($rgb)
{
$this->color = $rgb;
}
}
|
Ferrari.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;
/**
* Ferrari is a italian car
*/
class Ferrari implements VehicleInterface
{
/**
* @var string
*/
protected $color;
/**
* @param string $rgb
*/
public function setColor($rgb)
{
$this->color = $rgb;
}
}
|
1.3.4. 테스트¶
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 | <?php
namespace DesignPatterns\Creational\FactoryMethod\Tests;
use DesignPatterns\Creational\FactoryMethod\FactoryMethod;
use DesignPatterns\Creational\FactoryMethod\GermanFactory;
use DesignPatterns\Creational\FactoryMethod\ItalianFactory;
/**
* FactoryMethodTest tests the factory method pattern
*/
class FactoryMethodTest extends \PHPUnit_Framework_TestCase
{
protected $type = array(
FactoryMethod::CHEAP,
FactoryMethod::FAST
);
public function getShop()
{
return array(
array(new GermanFactory()),
array(new ItalianFactory())
);
}
/**
* @dataProvider getShop
*/
public function testCreation(FactoryMethod $shop)
{
// this test method acts as a client for the factory. We don't care
// about the factory, all we know is it can produce vehicle
foreach ($this->type as $oneType) {
$vehicle = $shop->create($oneType);
$this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\VehicleInterface', $vehicle);
}
}
/**
* @dataProvider getShop
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage spaceship is not a valid vehicle
*/
public function testUnknownType(FactoryMethod $shop)
{
$shop->create('spaceship');
}
}
|