Pattern: 簡單工廠模式
Class Diagram: 計算機
需求一:客戶想要一個加法計算機
| 12
 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;
 }
 }
 
 
 | 
需求二:客戶想要在既有的計算機上,增加減法功能
我們思路如下:
| 12
 3
 4
 5
 6
 
 | public function __construct($firstNumber, $secondNumber, $operationString){
 $this->firstNumber = $firstNumber;
 $this->secondNumber = $secondNumber;
 $this->operationString = $operationString;
 }
 
 | 
| 12
 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;
 }
 }
 
 | 
需求三:客戶想要除法的功能
| 12
 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,增加了嵌套層數。 
- 每當客戶要新增/修改/刪除一種運算時,其餘的運算都會被修改到。
 違反了開放封閉原則。
 
讓我們用簡單工廠改造它。
| 12
 3
 4
 5
 6
 7
 8
 
 | <?php
 namespace App\FactoryPattern\Calculator\Contracts;
 
 interface Operable
 {
 public function execute();
 }
 
 | 
| 12
 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;
 }
 }
 
 | 
| 12
 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;
 }
 }
 
 | 
| 12
 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;
 }
 }
 
 | 
| 12
 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;
 }
 }
 }
 
 | 
| 12
 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();
 }
 }
 
 | 
需求四:客戶想要乘法的功能
| 12
 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;
 }
 }
 
 | 
| 12
 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時,很可能就是使用工廠模式的時機。