享元模式 Flyweight Pattern 享元模式,在定義上來說是共享物件,將相似的物件集中整理,減少記憶體上的使用,舉例來說每座島的大頭菜鈴錢價格都不同,有些朋友會送你大頭菜,但因為朋友太多了,所以需要有個地方集中放這些大頭菜,並且記錄起來,每個朋友都送你一組大頭彩,但你不能重複紀錄,不然你只收到一組大頭菜,帳上卻紀錄兩組,這樣就不好了。
UML
實作 首先我們須要先定義大頭菜作為共享目標,紀錄了島嶼、鈴錢以及數量,並且提供了簡單的計算總價方法。
FlyweightInterface.php
1 2 3 4 5 6 7 8 9 10 interface FlyweightInterface { public function calculatePrice ( ): int ; }
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 class TurnipsFlyweight implements FlyweightInterface { protected $island ; protected $price ; protected $count ; public function __construct (string $island , int $price , int $count ) { $this ->island = $island ; $this ->price = $price ; $this ->count = $count ; } public function calculatePrice ( ): int { return $this ->price * $this ->count; } }
再來製作享元工廠,主要負責建立、管理大頭菜共享物件,以及提供基本功能,例如計算全部大頭菜總鈴錢的方法。
FlyweightFactory.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 use Countable ;class FlyweightFactory implements Countable { protected $turnips = []; public function get (string $island , int $price = 0 , int $count = 0 ): TurnipsFlyweight { if (!isset ($this ->turnips[$island ])) { $this ->turnips[$island ] = new TurnipsFlyweight ($island , $price , $count ); } return $this ->turnips[$island ]; } public function calculateTotal ( ): int { $total = 0 ; foreach ($this ->turnips as $turnip ) { $total += $turnip ->calculatePrice (); } return $total ; } public function count ( ): int { return count ($this ->turnips); } }
額外補充 Countable 繼承 Countable
這個類別可以使用 count()
這個方法,因此需要實作它。
1 2 3 4 class Countable { abstract public count ( void ) : int }
測試 最後我們要對享元物件、工廠進行簡單的測試,假設我們已經擁有了許多大頭菜,那麼我們有幾個需要做的檢查事項:
依序塞入享元,並且計算該次塞入的大頭菜跟紀錄所算出來的鈴錢是否相符。
大頭菜全部塞入後,所存入的筆數是否跟實際上相符。
最後計算所有大頭菜的總鈴錢是否是正確的。
FlyweightPatternTest.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 class FlyweightPatternTest extends TestCase { protected $turnips = array ( 'Island_A' => array ('price' => 90 , 'count' => 40 ), 'Island_B' => array ('price' => 92 , 'count' => 36 ), 'Island_C' => array ('price' => 94 , 'count' => 32 ), 'Island_D' => array ('price' => 96 , 'count' => 28 ), 'Island_E' => array ('price' => 98 , 'count' => 24 ), 'Island_F' => array ('price' => 100 , 'count' => 20 ), 'Island_G' => array ('price' => 102 , 'count' => 16 ), 'Island_H' => array ('price' => 104 , 'count' => 12 ), 'Island_I' => array ('price' => 106 , 'count' => 8 ), 'Island_J' => array ('price' => 108 , 'count' => 4 ), 'Island_K' => array ('price' => 110 , 'count' => 40 ), ); public function test_flyweight ( ) { $factory = new FlyweightFactory (); foreach ($this ->turnips as $key => $value ) { $flyweight = $factory ->get ($key , $value ['price' ], $value ['count' ]); $total = $flyweight ->calculatePrice (); $this ->assertEquals ($value ['price' ] * $value ['count' ], $total ); } $this ->assertCount (count ($this ->turnips), $factory ); $this ->assertEquals (25520 , $factory ->calculateTotal ()); } }
最後測試的執行結果會獲得如下:
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 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 ✔ ✔ ✔ ==> DataMapperTest ✔ ✔ ==> DecoratorPatternTest ✔ ✔ ==> DependencyInjectionTest ✔ ✔ ✔ ==> FacadePatternTest ✔ ==> FluentInterfaceTest ✔ ==> FlyweightPatternTest ✔ Time: 00:00.030, Memory: 6.00 MB OK (44 tests, 107 assertions)
完整程式碼 設計模式不難,找回快樂而已,以大頭菜為例。
參考文獻