轉接器模式 Adapter Pattern 轉接器模式,顧名思義會在兩個同功能但不同的規格的東西中,當作中間溝通的橋樑,就有點像是健康的大頭菜因為放超過一個禮拜,直接變成壞掉的大頭菜,兩個東西都是大頭菜,但規格上可能不太一樣,這時候我們就需要一個大頭菜轉接器,直接把健康的大頭菜給轉到壞掉。
UML
實作 首先我們需要先建立健康的大頭菜、以及壞掉的大頭菜,別忘記要建立介面(Interface)來定義大頭菜的規格。
TurnipsInterface.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 interface TurnipsInterface { public function risePrice (int $price ): int ; public function fallPrice (int $price ): int ; public function getPrice ( ): int ; public function setPrice (int $price ): int ; public function addCount (int $count ): int ; public function subCount (int $count ): int ; public function getCount ( ): int ; public function setCount (int $count ): int ; public function calculatePrice ( ): int ; }
Turnips.php
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 class Turnips implements TurnipsInterface { protected $price ; protected $count ; public function __construct (int $price , int $count ) { $this ->price = $price ; $this ->count = $count ; } public function risePrice (int $price ): int { $this ->price += $price ; return $this ->price; } public function fallPrice (int $price ): int { $this ->price -= $price ; return $this ->price; } public function getPrice ( ): int { return $this ->price; } public function setPrice (int $price ): int { $this ->price = $price ; return $this ->price; } public function addCount (int $count ): int { $this ->count += $count ; return $this ->count; } public function subCount (int $count ): int { $this ->count -= $count ; return $this ->count; } public function getCount ( ): int { return $this ->count; } public function setCount (int $count ): int { $this ->count = $count ; return $this ->count; } public function calculatePrice ( ): int { if (isset ($this ->price) && isset ($this ->count)) { return $this ->price * $this ->count; } else { return 0 ; } } }
SpoiledInterface.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface SpoiledInterface { public function risePrice (int $price ): int ; public function fallPrice (int $price ): int ; public function addCount (int $count ): int ; public function subCount (int $count ): int ; public function calculatePrice ( ): int ; }
Spoiled.php
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 class Spoiled implements SpoiledInterface { protected $price ; protected $count ; public function __construct (int $price , int $count ) { $this ->price = $price ; $this ->count = $count ; } public function risePrice (int $price ): int { $this ->price += $price ; return $this ->price; } public function fallPrice (int $price ): int { $this ->price -= $price ; return $this ->price; } public function addCount (int $count ): int { $this ->count += $count ; return $this ->count; } public function subCount (int $count ): int { $this ->count -= $count ; return $this ->count; } public function calculatePrice ( ): int { if (isset ($this ->price) && isset ($this ->count)) { return 0 * $this ->count; } else { return 0 ; } } }
再來我們需要為健康的大頭菜以及壞掉的大頭菜製作轉接器,概念上是引用壞掉的大頭菜介面,把健康的大頭菜丟進去,讓大頭菜以壞掉的方式來運作。
TurnipsAdapter.php
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 class TurnipsAdapter implements SpoiledInterface { protected $turnips ; public function __construct (TurnipsInterface $turnips ) { $this ->turnips = $turnips ; } public function risePrice (int $price ): int { $this ->turnips->setPrice (0 ); return $this ->turnips->getPrice (); } public function fallPrice (int $price ): int { $this ->turnips->setPrice (0 ); return $this ->turnips->getPrice (); } public function addCount (int $count ): int { $this ->turnips->addCount ($count ); return $this ->count; } public function subCount (int $count ): int { $this ->turnips->subCount ($count ); return $this ->turnips->getCount (); } public function calculatePrice ( ): int { if (isset ($this ->price) && isset ($this ->count)) { return 0 * $this ->count; } else { return 0 ; } } }
測試 寫完大頭菜轉接器以後,我們要來測試轉接器是否能夠正常使用,這裡會有幾個測試的重要項目:
測試大頭菜是否能夠正常賦予數量及價格,並且漲價 10 鈴錢、減少 20 組,最後算出價格是否符合。
測試大頭菜是否能夠正常賦予數量及價格,並且透過大頭菜轉接器把它轉成壞掉的大頭菜,最後漲價 10 鈴錢、減少 20 組,最後算出價格是否根本沒辦法賣鈴錢。
AdapterPatternTest.php
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 class AdapterPatternTest extends TestCase { public function test_can_rise_price_and_sub_count_on_turnips ( ) { $turnips = new Turnips (100 , 40 ); $turnips ->risePrice (10 ); $turnips ->subCount (20 ); $this ->assertEquals (2200 , $turnips ->calculatePrice ()); } public function test_can_rise_price_and_sub_count_on_spoiled ( ) { $turnips = new Turnips (100 , 40 ); $turnipsAdapter = new TurnipsAdapter ($turnips ); $turnipsAdapter ->risePrice (10 ); $turnipsAdapter ->subCount (20 ); $this ->assertEquals (0 , $turnipsAdapter ->calculatePrice ()); } }
最後測試的執行結果會獲得如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 PHPUnit Pretty Result Printer 0.28.0 by Codedungeon and contributors. ==> Configuration: ~/php-design-pattern/vendor/codedungeon/phpunit-result-printer/src/phpunit-printer.yml PHPUnit 9.2.6 by Sebastian Bergmann and contributors. ==> AbstractFactoryTest ✔ ✔ ✔ ✔ ==> BuilderPatternTest ✔ ✔ ==> FactoryMethodTest ✔ ✔ ✔ ✔ ==> PoolPatternTest ✔ ✔ ==> PrototypePatternTest ✔ ✔ ==> SimpleFactoryTest ✔ ✔ ✔ ✔ ==> SingletonPatternTest ✔ ==> StaticFactoryTest ✔ ✔ ✔ ✔ ✔ ==> AdapterPatternTest ✔ ✔ Time: 00:00.050, Memory: 6.00 MB OK (28 tests, 72 assertions)
完整程式碼 設計模式不難,找回快樂而已,以大頭菜為例。
參考文獻