組合模式 Composite Pattern 組合模式,一種將物件一個一個處理,並且最後組合起來的模式,可以想像剛買到大頭菜時的夢想,經過每次漲跌所帶來的希望與絕望,究竟是充滿絕望的遞減型呢?還是致富關鍵的三期型呢?每次的價格異動,都代表著價格物件,最終賣出的鈴錢價格,是經過許多鈴錢價格物件所算出來的。
UML
實作 首先我們要先定義組合介面,用來套用在每個功能物件上。
TurnipsInterface.php
1 2 3 4 5 6 7 interface TurnipsInterface { public function calculatePrice ( ): int ; }
再來建立大頭菜上漲(Price up)以及下跌(Price down)的物件,並且套用介面 Interface,其功能是把漲跌幅丟進去,用來紀錄當次的漲跌幅。
PriceUp.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 class PriceUp implements TurnipsInterface { protected $price ; public function __construct (int $price ) { $this ->price = $price ; } public function calculatePrice ( ): int { return $this ->price; } }
PriceDown.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 class PriceDown implements TurnipsInterface { protected $price ; public function __construct (int $price ) { $this ->price = $price ; } public function calculatePrice ( ): int { return - $this ->price; } }
最後建立大頭菜,賦予起始價格以及數量,然後可以賦予每次價格漲跌幅的物件,然後根據每個漲跌幅物件,來計算最終的鈴錢價格。
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 class Turnips implements TurnipsInterface { protected $price ; protected $count ; protected $elements = []; public function __construct (int $price , int $count ) { $this ->price = $price ; $this ->count = $count ; } public function calculatePrice ( ): int { $_price = $this ->price; foreach ($this ->elements as $element ) { $_price += $element ->calculatePrice (); } return $_price * $this ->count; } public function addElement (TurnipsInterface $element ) { array_push ($this ->elements, $element ); } }
測試 最後我們需要將大頭菜組合模式來寫些測試驗證一下,主要是測試鈴錢價格上漲物件、鈴錢價格下跌物件是否能夠正常運作,再來實際組裝起來再各別計算一次。
測試鈴錢價格上漲物件是否能夠正常運作。
測試鈴錢價格下跌物件是否能夠正常運作。
測試鈴錢價格上漲物件、鈴錢價格下跌物件實際組裝起來再各別計算一次。
CompositePatternTest.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 class CompositePatternTest extends TestCase { public function test_price_up ( ) { $price = new PriceUp (20 ); $this ->assertEquals (20 , $price ->calculatePrice ()); } public function test_price_down ( ) { $price = new PriceDown (20 ); $this ->assertEquals (-20 , $price ->calculatePrice ()); } public function test_price_up_and_down ( ) { $turnips = new Turnips (100 , 40 ); $this ->assertEquals (4000 , $turnips ->calculatePrice ()); $turnips ->addElement (new PriceUp (20 )); $this ->assertEquals (4800 , $turnips ->calculatePrice ()); $turnips ->addElement (new PriceDown (30 )); $this ->assertEquals (3600 , $turnips ->calculatePrice ()); } }
最後測試的執行結果會獲得如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 ✔ ✔ ✔ ==> CompositePatternTest ✔ ✔ ✔ Time: 00:00.050, Memory: 6.00 MB OK (34 tests, 81 assertions)
完整程式碼 設計模式不難,找回快樂而已,以大頭菜為例。
參考文獻