範例:運輸系統 (抽象工廠模式)

Pattern: 抽象工廠模式

Class Diagram: 運輸系統


前情提要:鐵路運輸系統,參考範例:運輸系統(工廠方法模式)

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\AbstractFactoryPattern;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use ReflectionClass;

class Program
{
/**
* @param string $modelName
* @return string
*/
public function getModel($modelName)
{
$modelFactory = $this->createModelFactory($modelName);
$model = $modelFactory->createModel();
return $model->getName();
}

/**
* @param string $modelName
* @return ModelFactory
*/
private function createModelFactory($modelName)
{
$namespace = 'App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories';
$className = $modelName . 'Factory';

$reflector = new ReflectionClass($namespace . '\\' . $className);
return $reflector->newInstance();
}
}

需求一:各交通工具準備就緒,要開始處理內裝問題。

由於不同的機型會有不同的內裝,讓我們從定義椅子介面開始。

  • 定義椅子介面
1
2
3
4
5
6
7
8
9
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts;

interface Chair
{
public function getName();
}

  • 接著改寫機型工廠介面,
    除了製作機型外,要新增製作椅子的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;

interface ModelFactory
{
public function createModel(): Model;

public function createChair(): Chair;
}

  • 實作長型座椅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Chair;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;

class LongChair implements Chair
{
public function getName()
{
return '長型座椅';
}
}

  • 實作對號座椅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Chair;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;

class ReservedSeatChair implements Chair
{
public function getName()
{
return '對號座椅';
}
}

  • 實作飛機座椅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Chair;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;

class PlaneChair implements Chair
{
public function getName()
{
return '飛機座椅';
}
}


接著改寫各個機型工廠,選擇適合的座椅。

  • 改寫區間車工廠
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
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\LocalTrain;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\LongChair;

class LocalTrainFactory implements ModelFactory
{
public function createModel(): Model
{
//取得生產材料...
//招募技術團隊...
return new LocalTrain();
}

public function createChair(): Chair
{
return new LongChair();
}
}

  • 改寫復興號工廠
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
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\SemiExpress;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\ReservedSeatChair;

class SemiExpressFactory implements ModelFactory
{
public function createModel(): Model
{
//取得生產材料...
//招募技術團隊...
return new SemiExpress();
}

public function createChair(): Chair
{
return new ReservedSeatChair();
}
}

  • 改寫自強號工廠
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 App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\LimitedExpress;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\ReservedSeatChair;

class LimitedExpressFactory implements ModelFactory
{
public function createModel(): Model
{
//取得生產材料...
//招募技術團隊...
return new LimitedExpress();
}

public function createChair(): Chair
{
return new ReservedSeatChair();
}
}
  • 改寫波音747工廠
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
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\Boeing747;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\PlaneChair;

class Boeing747Factory implements ModelFactory
{
public function createModel(): Model
{
//取得生產材料...
//招募技術團隊..
return new Boeing747();
}

public function createChair(): Chair
{
return new PlaneChair();
}
}

  • 最後改寫原本的程式碼
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
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use ReflectionClass;

class Program
{
/**
* @param string $modelName
* @return string
*/
public function getModel($modelName)
{
$modelFactory = $this->createModelFactory($modelName);
$model = $modelFactory->createModel();
return $model->getName();
}

/**
* @param string $modelName
* @return string
*/
public function getChair($modelName)
{
$modelFactory = $this->createModelFactory($modelName);
$chair = $modelFactory->createChair();
return $chair->getName();
}

/**
* @param string $modelName
* @return ModelFactory
*/
private function createModelFactory($modelName)
{
$namespace = 'App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories';
$className = $modelName . 'Factory';

$reflector = new ReflectionClass($namespace . '\\' . $className);
return $reflector->newInstance();
}
}

透過工廠介面,我們組合了各種類型產品。
使得不同類型的產品之間,有了產品族的聯繫關係。


[單一職責原則]
我們將工廠類別 (Creator)產品類別 (Product) 視作兩種不同的職責。

[開放封閉原則]
當新增新的產品族時,我們僅需新增工廠類別。
當修改既有產品時,我們僅需修改其產品類別,不會影響到其他的產品類別。

[介面隔離原則]
工廠介面:用來創建機型及座椅。
產品介面:用來創建對應的產品。

[依賴反轉原則]
工廠介面依賴於抽象的機型介面與座椅介面。

工廠實作抽象的工廠介面。
機型實作抽象的機型介面。
座椅實作抽象的座椅介面。

ʕ •ᴥ•ʔ:雖然有點複雜,但這個模式好美。