範例:運輸系統(簡單工廠模式)

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
<?php

namespace App\FactoryPattern\Transport\SimpleFactoryPattern;

class Program
{
/**
* @param string $modelName
* @return string
*/
public function getModel($modelName)
{
switch ($modelName) {
case 'LocalTrain':
return '區間車';
break;

case 'LimitedExpress':
return '自強號';
break;
}
}
}


需求一:新增復興號 (SemiExpress)

正當我們想直接在switch中新增一個case時,
發現這種做法違反了開放封閉原則

明明是新增機型,卻修改到了既有的程式碼(取得機型名稱)。
此外在修改機型名稱時,也會修改到既有程式碼。

讓我們用簡單工廠模式,將創建物件物件本身的職責分離。
(註:客戶端的程式碼、創建機型、機型本身,共三種職責)

  • 首先定義機型介面
1
2
3
4
5
6
7
8
<?php

namespace App\FactoryPattern\Transport\SimpleFactoryPattern\Contracts;

interface Model
{
public function getName();
}
  • 實作區間車機型
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

namespace App\FactoryPattern\Transport\SimpleFactoryPattern\Model;

use App\FactoryPattern\Transport\SimpleFactoryPattern\Contracts\Model;

class LocalTrain implements Model
{
public function getName()
{
return '區間車';
}
}
  • 實作自強號機型
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

namespace App\FactoryPattern\Transport\SimpleFactoryPattern\Model;

use App\FactoryPattern\Transport\SimpleFactoryPattern\Contracts\Model;

class LimitedExpress implements Model
{
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
26
27
<?php

namespace App\FactoryPattern\Transport\SimpleFactoryPattern;

use App\FactoryPattern\Transport\SimpleFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\SimpleFactoryPattern\Model\LocalTrain;
use App\FactoryPattern\Transport\SimpleFactoryPattern\Model\LimitedExpress;

class RailWayModelFactory
{
/**
* @param string $modelName
* @return Model
*/
public function createModel($modelName): Model
{
switch ($modelName) {
case 'LocalTrain':
return new LocalTrain();
break;

case 'LimitedExpress':
return new LimitedExpress();
break;
}
}
}
  • 接著是修改原本的程式
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
{
/**
* @param string $modelName
* @return string
*/
public function getModel($modelName)
{
$railwayModelFactory = new RailwayModelFactory();
$model = $railwayModelFactory->createModel($modelName);
return $model->getName();
}
}
  • 回到需求,實作復興號機型
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

namespace App\FactoryPattern\Transport\SimpleFactoryPattern\Model;

use App\FactoryPattern\Transport\SimpleFactoryPattern\Contracts\Model;

class SemiExpress implements Model
{
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
26
27
28
29
30
31
32
<?php

namespace App\FactoryPattern\Transport\SimpleFactoryPattern;

use App\FactoryPattern\Transport\SimpleFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\SimpleFactoryPattern\Model\LocalTrain;
use App\FactoryPattern\Transport\SimpleFactoryPattern\Model\LimitedExpress;
use App\FactoryPattern\Transport\SimpleFactoryPattern\Model\SemiExpress;

class RailWayModelFactory
{
/**
* @param string $model
* @return Model
*/
public function createModel($model): Model
{
switch ($model) {
case 'LocalTrain':
return new LocalTrain();
break;

case 'LimitedExpress':
return new LimitedExpress();
break;

case 'SemiExpress':
return new SemiExpress();
break;
}
}
}

[單一職責原則]

  1. 我們將 創建類別 (Creator)產品類別 (Product) 視作兩種不同的職責。
  2. 將取得機型資訊的運輸系統視作第三種職責。

[開放封閉原則]

  1. 新增新的產品時,我們僅需新增產品類別修改創建類別
  2. 修改既有產品時,我們僅需修改其產品類別創建類別
    不會影響到其他的產品類別。
  3. 無論是新增/修改產品,我們都不用再去修改到運輸系統類別

[依賴反轉原則]

  1. 運輸類別依賴抽象的機型介面。
  2. 產品類別實作抽象的機型介面。

透過簡單工廠模式,運輸系統類別減少了改變的機會,
但創建類別依然常常需要變更。

ʕ •ᴥ•ʔ:不久之後,我們還會見到這個範例,敬請期待。