橋接模式 Bridge Pattern 橋接模式,將實作體系與抽象體系分離開來,讓兩者能各自更動各自演進,就有點像是大頭菜有分健康的大頭菜及壞掉的大頭菜,你的島上有這兩種大頭菜,但是健康的大頭菜過了一個禮拜都沒賣掉的話,他就會變成壞掉的大頭菜了。
UML
實作 首先我們要來把大頭菜做出來,因此會有定義大頭菜介面 Interface、建立健康的大頭菜及壞掉的大頭菜。
TurnipsInterface.php
1 2 3 4 5 6 7 interface TurnipsInterface { 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 class Turnips implements TurnipsInterface { protected $price ; protected $count ; public function __construct (int $price , int $count ) { $this ->price = $price ; $this ->count = $count ; } public function calculatePrice ( ): int { if (isset ($this ->price) && isset ($this ->count)) { return $this ->price * $this ->count; } else { return 0 ; } } }
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 class Spoiled implements TurnipsInterface { protected $price ; protected $count ; public function __construct (int $price , int $count ) { $this ->price = $price ; $this ->count = $count ; } public function calculatePrice ( ): int { if (isset ($this ->price) && isset ($this ->count)) { return 0 * $this ->count; } else { return 0 ; } } }
接下來我們要建立所謂的橋樑,這裡有個重要的地方是實作(Implementation),功能是來去替換大頭菜的類別。
BaseService.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 abstract class BaseService { protected $implementation ; public function __construct (TurnipsInterface $turnips ) { $this ->implementation = $turnips ; } public function setImplementation (TurnipsInterface $turnips ) { $this ->implementation = $turnips ; } abstract public function calculatePrice ( ) ; }
TurnipsService.php
1 2 3 4 5 6 7 8 9 10 11 12 13 class TurnipsService extends BaseService { public function calculatePrice ( ) { return $this ->implementation->calculatePrice (); } }
測試 最後我們要來寫幾些測試項目,主要測試建立 Service
並且執行計算,以及是否能夠替換大頭菜類別,並且計算。
測試是否能建立 Service 並把健康的大頭菜塞進去,然後計算鈴錢。
測試是否能建立 Service 並把壞掉的大頭菜塞進去,然後計算鈴錢。
測試是否能建立 Service 並把健康的大頭菜塞進去,然後計算鈴錢,再把大頭菜替換成壞掉的大頭菜,再計算一次鈴錢。
BridgePatternTest.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 class BridgePatternTest extends TestCase { public function test_can_calculate_price_for_turnips ( ) { $service = new TurnipsService (new Turnips (100 , 40 )); $this ->assertEquals (4000 , $service ->calculatePrice ()); } public function test_can_calculate_price_for_spoiled ( ) { $service = new TurnipsService (new Spoiled (100 , 40 )); $this ->assertEquals (0 , $service ->calculatePrice ()); } public function test_can_calculate_price_for_turnips_and_spoiled ( ) { $service = new TurnipsService (new Turnips (100 , 40 )); $this ->assertEquals (4000 , $service ->calculatePrice ()); $service ->setImplementation (new Spoiled (100 , 40 )); $this ->assertEquals (0 , $service ->calculatePrice ()); } }
最後測試的執行結果會獲得如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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 ✔ ✔ ==> BridgePatternTest ✔ ✔ ✔ Time: 00:00.027, Memory: 6.00 MB OK (31 tests, 76 assertions)
完整程式碼 設計模式不難,找回快樂而已,以大頭菜為例。
參考文獻