模式概念
工厂模式关注于抽象类型时如何解决创建对象实例的问题,通过子类实例化具体的类。
UML类图
代码实现
VehicleInterface.php
<?php
namespace DesignPatterns\Creational\FactoryMethod;
interface VehicleInterface
{
public function setColor(string $rgb);
}
CarMerecedes.php
<?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
<?php
namespace DesignPatterns\Creational\FactoryMethod;
class CarFerrari implements VehicleInterface
{
/**
* @var string
*/
private $color;
public function setColor(string $rgb)
{
$this->color = $rgb;
}
}
Bicycle.php
<?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
}
}
FactroyMethod.php
<?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;
}
}
ItalianFactroy.php
<?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");
}
}
}
GermanFactroy.php
<?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");
}
}
}
FactroyMethodTest.php
<?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');
}
}
心得总结:
- 该模型有四种角色:抽象工厂、具体工厂、抽象产品、具体产品;具体工厂都是进行具体产品对象的实例;
- 工厂模型运行系统在不改变工厂角色和产品实现,再继续增加具体的工厂和具体的产品;