備忘錄模式 Memento Pattern 備忘錄模式,在不破壞封裝物件的前提之下,提供物件一個「皇后殺手 第三爆彈:敗者成塵」的能力,物件在極度絕望的狀態下,把當前物件炸光光,並令時間往前倒退至上一個時空紀錄點的設計模式,跟吉良吉影的不同點在於命運會跟著被改變,被破壞的東西會恢復原狀。
UML
實作 為了讓大頭菜有爆掉並往前倒退至上一個時空紀錄點的功能,這次我們要先實作備忘錄模式,用來儲存大頭菜的時空狀態,好讓大頭菜可以回到過去。
大頭菜:「キラークイーン、バイツ・ザ・ダスト!」
Memento.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 class Memento { protected int $price ; protected int $count ; public function __construct (int $price , int $count ) { $this ->price = $price ; $this ->count = $count ; } public function getPrice ( ): int { return $this ->price; } public function getCount ( ): int { return $this ->count; } }
接下來要實作擁有第三爆彈的大頭菜,除了以往所擁有的資訊、方法以外,這次要提供儲存、載入的功能,讓大頭菜能在任意的時空點進行儲存,並且在自己被逼到極限時,按下第三爆彈穿越時空回到過去。
大頭菜:「到極限了,就是現在,按下去!」
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 class Turnips { protected int $price ; protected int $count ; public function __construct (int $price , int $count ) { $this ->price = $price ; $this ->count = $count ; } public function saveToMemento ( ): Memento { return new Memento ($this ->price, $this ->count); } public function restoreFromMemento (Memento $memento ) { $this ->price = $memento ->getPrice (); $this ->count = $memento ->getCount (); } public function getPrice ( ): int { return $this ->price; } public function getCount ( ): int { return $this ->count; } public function setPrice (int $price ) { $this ->price = $price ; } public function setCount (int $count ) { $this ->count = $count ; } public function calculatePrice ( ): int { return $this ->getPrice () * $this ->getCount (); } }
測試 接下來我們要替第三爆彈模式測試一下,要讓大頭菜真的儲存某個時空點,並且按下爆彈炸毀當前物件,然後飛躍時空回到過去。
MementoPatternTest.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 class MementoPatternTest extends TestCase { public function test_bites_the_dust ( ) { $turnips = new Turnips (100 , 40 ); $this ->assertEquals (4000 , $turnips ->calculatePrice ()); $memento = $turnips ->saveToMemento (); $newPrice = random_int (0 , 600 ); $turnips ->setPrice ($newPrice ); $this ->assertEquals ($newPrice * 40 , $turnips ->calculatePrice ()); $turnips ->restoreFromMemento ($memento ); $this ->assertEquals (4000 , $turnips ->calculatePrice ()); } }
最後測試的執行結果會獲得如下:
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 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. ==> ...fResponsibilitiesTest ✔ ✔ ✔ ==> CommandPatternTest ✔ ==> IteratorPatternTest ✔ ✔ ✔ ✔ ==> MediatorPatternTest ✔ ✔ ✔ ==> MementoPatternTest ✔ ==> AbstractFactoryTest ✔ ✔ ✔ ✔ ==> BuilderPatternTest ✔ ✔ ✔ ✔ ==> FactoryMethodTest ✔ ✔ ✔ ✔ ==> PoolPatternTest ✔ ✔ ==> PrototypePatternTest ✔ ✔ ==> SimpleFactoryTest ✔ ✔ ✔ ✔ ==> SingletonPatternTest ✔ ==> StaticFactoryTest ✔ ✔ ✔ ✔ ✔ ==> AdapterPatternTest ✔ ✔ ==> BridgePatternTest ✔ ✔ ✔ ==> CompositePatternTest ✔ ✔ ✔ ==> DataMapperTest ✔ ✔ ==> DecoratorPatternTest ✔ ✔ ==> DependencyInjectionTest ✔ ✔ ✔ ==> FacadePatternTest ✔ ==> FluentInterfaceTest ✔ ==> FlyweightPatternTest ✔ ==> ProxyPatternTest ✔ ✔ ==> RegistryPatternTest ✔ ✔ ✔ ✔ ✔ Time: 00:00.105, Memory: 6.00 MB OK (63 tests, 132 assertions)
完整程式碼 設計模式不難,找回快樂而已,以大頭菜為例。
參考文獻