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\Calculator;
class Program {
private $firstNumber;
private $secondNumber;
private $operationString;
public function __construct($firstNumber, $secondNumber) { $this->firstNumber = $firstNumber; $this->secondNumber = $secondNumber; }
public function run() { return $this->firstNumber + $this->secondNumber; } }
|
需求二:客戶想要在既有的計算機上,增加減法功能
我們思路如下:
1 2 3 4 5 6
| public function __construct($firstNumber, $secondNumber, $operationString) { $this->firstNumber = $firstNumber; $this->secondNumber = $secondNumber; $this->operationString = $operationString; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public function run() { $operationString = $this->operationString;
switch ($operationString) { case '+': return $this->firstNumber + $this->secondNumber; break;
case '-': return $this->firstNumber - $this->secondNumber; break; } }
|
需求三:客戶想要除法的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public function run() { $operationString = $this->operationString;
switch ($operationString) { case '+': return $this->firstNumber + $this->secondNumber; break;
case '-': return $this->firstNumber - $this->secondNumber; break;
case "/": if ($this->secondNumber === 0) { return 'Division by zero'; } return $this->firstNumber / $this->secondNumber; break; } }
|
這時候功能是完成了,但有沒有覺得哪裡怪怪的?
除法的case很醜,要額外判斷除數不為0,增加了嵌套層數。
每當客戶要新增/修改/刪除一種運算時,其餘的運算都會被修改到。
違反了開放封閉原則。
讓我們用簡單工廠改造它。
1 2 3 4 5 6 7 8
| <?php
namespace App\FactoryPattern\Calculator\Contracts;
interface Operable { public function execute(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php
namespace App\FactoryPattern\Calculator\Operation;
use App\FactoryPattern\Calculator\Contracts\Operable;
class AddOperation implements Operable { public $firstNumber; public $secondNumber;
public function execute() { return $this->firstNumber + $this->secondNumber; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php
namespace App\FactoryPattern\Calculator\Operation;
use App\FactoryPattern\Calculator\Contracts\Operable;
class MinusOperation implements Operable { public $firstNumber; public $secondNumber;
public function execute() { return $this->firstNumber - $this->secondNumber; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php
namespace App\FactoryPattern\Calculator\Operation;
use App\FactoryPattern\Calculator\Contracts\Operable;
class DivideOperation implements Operable { public $firstNumber; public $secondNumber;
public function execute() { if ($this->secondNumber === 0) { return 'Division by zero'; } return $this->firstNumber / $this->secondNumber; } }
|
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\Calculator;
use App\FactoryPattern\Calculator\Contracts\Operable; use App\FactoryPattern\Calculator\Operation\AddOperation; use App\FactoryPattern\Calculator\Operation\MinusOperation; use App\FactoryPattern\Calculator\Operation\DivideOperation;
class SimpleOperationFactory {
public function create($operationString): Operable { switch ($operationString) { case '+': return new AddOperation(); break;
case '-': return new MinusOperation(); break;
case '/': return new DivideOperation(); break; } } }
|
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 51 52 53 54
| <?php
namespace App\FactoryPattern\Calculator;
use App\FactoryPattern\Calculator\Contracts\Operable; use App\FactoryPattern\Calculator\SimpleOperationFactory;
class Program {
private $firstNumber;
private $secondNumber;
private $operationString;
private $operation;
public function __construct($firstNumber, $secondNumber, $operationString) { $this->firstNumber = $firstNumber; $this->secondNumber = $secondNumber; $this->operationString = $operationString; }
public function run() { $simpleOperationFactory = new SimpleOperationFactory(); $this->operation = $simpleOperationFactory->create($this->operationString);
return $this->execute(); }
private function execute() { $this->operation->firstNumber = $this->firstNumber; $this->operation->secondNumber = $this->secondNumber; return $this->operation->execute(); } }
|
需求四:客戶想要乘法的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php
namespace App\FactoryPattern\Calculator\Operation;
use App\FactoryPattern\Calculator\Contracts\Operable;
class MultiplyOperation implements Operable { public $firstNumber; public $secondNumber;
public function execute() { return $this->firstNumber * $this->secondNumber; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public function create($operationString): Operable { switch ($operationString) { case '+': return new AddOperation(); break;
case '-': return new MinusOperation(); break;
case '/': return new DivideOperation(); break;
case '*': return new MultiplyOperation(); break; } }
|
[單一職責原則]
將創造運算的職責跟運算本身的職責分離,就是工廠模式的精神!
[開放封閉原則]
這下子,我們終於不會每次新增/修改/刪除一個運算時,
影響到其餘所有運算了。
ʕ •ᴥ•ʔ:函式出現switch時,很可能就是使用工廠模式的時機。