Pattern: 工廠方法模式
Class Diagram: 運輸系統
前情提要:鐵路運輸系統,參考範例:運輸系統(簡單工廠模式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php
namespace App\FactoryPattern\Transport\SimpleFactoryPattern;
use App\FactoryPattern\Transport\SimpleFactoryPattern\RailwayModelFactory;
class Program {
public function getModel($modelName) { $railwayModelFactory = new RailwayModelFactory(); $model = $railwayModelFactory->createModel($modelName); return $model->getName(); } }
|
需求一:隨著公司擴大,運輸系統不再僅限鐵路,將引進波音747,進軍航空業
此時發現,原本的簡單工廠,
從創建對象都是火車,到有飛機,
邏輯越來越複雜了。
常常違反開放封閉原則,
讓我們用工廠方法模式修正它。
- 首先定義機型工廠介面(可生產火車、飛機等各種機型)
1 2 3 4 5 6 7 8 9 10
| <?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\Contracts;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model;
interface ModelFactory { public function createModel(): Model; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model; use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\ModelFactory; use App\FactoryPattern\Transport\FactoryMethodPattern\Model\LocalTrain;
class LocalTrainFactory implements ModelFactory { public function createModel(): Model { return new LocalTrain(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model; use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\ModelFactory; use App\FactoryPattern\Transport\FactoryMethodPattern\Model\SemiExpress;
class SemiExpressFactory implements ModelFactory { public function createModel(): Model { return new SemiExpress(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model; use App\FactoryPattern\Transport\FactoryMethodPattern\Model\LimitedExpress; use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\ModelFactory;
class LimitedExpressFactory implements ModelFactory { public function createModel(): Model { return new LimitedExpress(); } }
|
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
| <?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern;
use App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories\LimitedExpressFactory; use App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories\LocalTrainFactory; use App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories\SemiExpressFactory;
class Program {
public function getModel($modelName) { $modelFactory = $this->createModelFactory($modelName); $model = $modelFactory->createModel(); return $model->getName(); }
private function createModelFactory($modelName) { switch ($modelName) { case 'LimitedExpress': return new LimitedExpressFactory(); break;
case 'LocalTrain': return new LocalTrainFactory(); break;
case 'SemiExpress': return new SemiExpressFactory(); break; } } }
|
- 運用反射 (Reflection) 機制,讓客戶端的程式碼不再修改
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
| <?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\ModelFactory; use ReflectionClass;
class Program {
public function getModel($modelName) { $modelFactory = $this->createModelFactory($modelName); $model = $modelFactory->createModel(); return $model->getName(); }
private function createModelFactory($modelName) { $namespace = 'App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories'; $className = $modelName . 'Factory';
$reflector = new ReflectionClass($namespace . '\\' . $className); return $reflector->newInstance(); } }
|
這下子,改造工程結束,讓我們回到波音747!
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\Model;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model;
class Boeing747 implements Model { public function getName() { return '波音747'; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\ModelFactory; use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model; use App\FactoryPattern\Transport\FactoryMethodPattern\Model\Boeing747;
class Boeing747Factory implements ModelFactory { public function createModel(): Model { return new Boeing747(); } }
|
[單一職責原則]
我們將創建類別 (Creator) 與 產品類別 (Product) 視作兩種不同的職責。
[開放封閉原則]
當新增新的產品時,我們僅需新增產品類別及新增創建類別。
當修改既有產品時,我們僅需修改其產品類別,不會影響到其他的產品類別。
[介面隔離原則]
拆分出工廠介面與機型介面。
[依賴反轉原則]
客戶依賴於抽象工廠介面與機型介面。
工廠類別實作抽象的工廠介面。
機型類別實作抽象的機型介面。
ʕ •ᴥ•ʔ:不久之後,我們會再一次看到這個範例,敬請期待。