註冊模式 Registry Pattern 註冊模式,如果應用程式內有非常多同樣的物件需要高度重複讀寫,就會去建立一個儲存器來負責管理這些同樣的物件,就有點像是你的大頭菜,會來自不同的島,每座的島菜價不同,這會導致你很難算出所賺到的鈴錢,所以如果每個大頭菜都需要登記註冊,然後有個集中管理的名冊,在管理大頭菜這件事上就能比較輕鬆。
UML
實作 首先我們會先建立需要被集中管理的大頭菜物件,裡面提供了簡單的幾些方法,例如賦予獲得島嶼名稱、鈴錢以及數量。
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 class Turnips { public string $island ; public int $price ; public int $count ; public function __construct (string $island , int $price , int $count ) { $this ->setIsland ($island ); $this ->setPrice ($price ); $this ->setCount ($count ); } public function setIsland (string $island ) { $this ->island = $island ; } public function setPrice (int $price ) { $this ->price = $price ; } public function setCount (int $count ) { $this ->count = $count ; } public function getIsland ( ): string { return $this ->island; } public function getPrice ( ): int { return $this ->price; } public function getCount ( ): int { return $this ->count; } }
再來我們需要建立一個集中管理大頭菜的抽象註冊器,無論是找尋(find...)註冊、更新或刪除,這些事情都會集中在這裡實踐。
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 abstract class Registry { protected static array $turnips = []; public static function findTurnipsByIsland (string $island ) { foreach (self ::$turnips as $turnip ) { if ($island === $turnip ->getIsland ()) { return $turnip ; } } throw new InvalidArgumentException ('大頭菜儲存容器找不到目標。' ); } public static function findIndexByIsland (string $island ) { foreach (self ::$turnips as $index => $turnip ) { if ($island === $turnip ->getIsland ()) { return $index ; } } return null ; } public static function store (Turnips $turnip ) { if (self ::findIndexByIsland ($turnip ->getIsland ())) { throw new InvalidArgumentException ('大頭菜儲存容器已經包含這顆大頭菜了。' ); } array_push (self ::$turnips , $turnip ); } public static function update (Turnips $turnips ) { if ($index = self ::findIndexByIsland ($turnips ->getIsland ())) { self ::$turnips [$index ]->setPrice ($turnips ->getPrice ()); self ::$turnips [$index ]->setCount ($turnips ->getCount ()); return ; } throw new InvalidArgumentException ('大頭菜儲存容器找不到目標。' ); } public static function destroy (Turnips $turnips ) { if ($index = self ::findIndexByIsland ($turnips ->getIsland ())) { unset (self ::$turnips [$index ]); return ; } throw new InvalidArgumentException ('大頭菜儲存容器找不到目標。' ); } }
測試 註冊器寫完以後,雖然程式很簡短,但需要做的測試有不少,會有幾個需要做的測試項目:
測試是否能夠建立大頭菜,放入後並取出是稍早建立的大頭菜。
測試連續建立重複的大頭菜是否會獲得錯誤。
測試直接獲取不存在的大頭菜索引是否會獲得空值。
測試建立大頭菜並更新大頭菜之後,大頭菜資訊是否有正確更新。
測試大頭菜是否能夠被移除。
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 class RegistryPatternTest extends TestCase { public function test_registry_store ( ) { $turnips = new Turnips ('Island_A' , 100 , 40 ); Registry ::store ($turnips ); $this ->assertSame ($turnips , Registry ::findTurnipsByIsland ('Island_A' )); } public function test_registry_store_exception ( ) { $this ->expectException (\InvalidArgumentException ::class ); $turnips = new Turnips ('Island_B' , 100 , 40 ); Registry ::store ($turnips ); Registry ::store ($turnips ); } public function test_registry_get_exception ( ) { $this ->assertNull (Registry ::findIndexByIsland ('Island_Null' )); } public function test_registry_update ( ) { $turnips = new Turnips ('Island_C' , 100 , 40 ); Registry ::store ($turnips ); $turnips ->setPrice (90 ); $turnips ->setCount (20 ); Registry ::update ($turnips ); $this ->assertSame ($turnips , Registry ::findTurnipsByIsland ('Island_C' )); } public function test_registry_destroy ( ) { $turnips = new Turnips ('Island_D' , 100 , 40 ); Registry ::store ($turnips ); $this ->assertSame ($turnips , Registry ::findTurnipsByIsland ('Island_D' )); $this ->expectException (\InvalidArgumentException ::class ); Registry ::destroy ($turnips ); Registry ::findTurnipsByIsland ('Island_D' ); } }
最後測試的執行結果會獲得如下:
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 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 ✔ ==> ProxyPatternTest ✔ ✔ ==> RegistryPatternTest ✔ ✔ ✔ ✔ ✔ Time: 00:00.037, Memory: 6.00 MB OK (51 tests, 116 assertions)
完整程式碼 設計模式不難,找回快樂而已,以大頭菜為例。
參考文獻