備忘錄,這個名字其實就已經很形象的解釋了它的做用。典型的例子就是咱們原來玩硬盤遊戲時的存檔功能。當你對即將面對的大BOSS有所顧慮時,通常都會先保存一次進度存檔。若是挑戰失敗了,直接讀取存檔就能夠恢復到挑戰BOSS前的狀態,而後你就開開心心的再去練一會級回來解決這個大BOSS就行了。不過,爲了以防萬一,在挑戰BOSS以前存個檔老是好的。另一個例子就是咱們碼農們每天要用到的代碼管理工具Git或者Svn了。每次的提交都像是一次存檔備份,當新代碼出現問題的時候,直接回滾恢復就好了。這些,都是備忘錄模式的典型應用,下面就一塊兒來看看這個模式吧。php
GoF定義:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。這樣之後就可將該對象恢復到原先保存的狀態git
GoF類圖github
代碼實現數據庫
class Originator { private $state; public function SetMeneto(Memento $m) { $this->state = $m->GetState(); } public function CreateMemento() { $m = new Memento(); $m->SetState($this->state); return $m; } public function SetState($state) { $this->state = $state; } public function ShowState() { echo $this->state, PHP_EOL; } }
原發器,也能夠叫作發起人。它有一個內部狀態(state),這個狀態能夠在不一樣的狀況下進行改變。當某一個事件發生時,須要將這個狀態恢復到原先的狀態。在這裏,咱們有一個CreateMemento()用於建立一個備忘錄(存檔),有一個SetMeneto()用於還原狀態(讀檔)。windows
class Memento { private $state; public function SetState($state) { $this->state = $state; } public function GetState() { return $this->state; } }
備忘錄,很是簡單,就是用於記錄狀態。將這個狀態以對象的形式保存,就可讓原發器很是方便地建立不少存檔用於記錄各類不一樣的狀態。瀏覽器
class Caretaker { private $memento; public function SetMemento($memento) { $this->memento = $memento; } public function GetMemento() { return $this->memento; } }
負責人,也叫作管理者類,保存備忘錄,當須要的時候從這裏取出備忘錄。它只負責保存,不能修改備忘錄。在複雜的應用中,能夠將這裏作成列表,就像遊戲中能夠選擇性的展示多條存檔記錄供玩家選擇。緩存
$o = new Originator(); $o->SetState('狀態1'); $o->ShowState(); // 保存狀態 $c = new Caretaker(); $c->SetMemento($o->CreateMemento()); $o->SetState('狀態2'); $o->ShowState(); // 還原狀態 $o->SetMeneto($c->GetMemento()); $o->ShowState();
客戶端的調用中,咱們的原發器初始化狀態後進行了保存,而後人爲的更改了狀態。這時只須要經過負責人將狀態還原回來就能夠了。服務器
Mac的時光機功能你們有了解過吧,能夠將電腦恢復到某一時間點的狀態下。其實windows的ghost也是相似的功能。咱們的手機操做系統上也決定開發這樣的一個功能。當咱們點擊時光機備份時,將手機上全部的資料、數據、狀態信息都壓縮保存起來,若是用戶容許的話,咱們將這個壓縮包上傳到咱們的雲服務器上避免佔用用戶的手機內存,不然就只能保存到用戶的手機內存中了。當用戶的手機須要恢復到某個時間點,咱們將全部的時光機備份列出,用戶只須要用手指輕輕一按就能夠把手機系統狀態恢復到當時的樣子了,是否是很是方便!!微信
完整代碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/17.memento/source/memento.php網絡
此次又回到短信發送的例子上來。一般咱們作短信或者郵件發送這些功能時,會有一個隊列從數據庫或者緩存中讀取要發送的內容進行發送,若是成功了就無論了,若是失敗了會將短信的狀態改爲失敗或者重發。在這裏,咱們直接將它改回到以前未發送的狀態而後等待下次發送的隊列再次執行發送。
短信發送類圖
完整源碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/17.memento/source/memento-message.php
<?php class Message { private $content; private $to; private $state; private $time; public function __construct($to, $content) { $this->to = $to; $this->content = $content; $this->state = '未發送'; $this->time = time(); } public function Show() { echo $this->to, '---', $this->content, '---', $this->time, '---', $this->state, PHP_EOL; } public function CreateSaveSate() { $ss = new SaveState(); $ss->SetState($this->state); return $ss; } public function SetSaveState($ss) { if ($this->state != $ss->GetState()) { $this->time = time(); } $this->state = $ss->GetState(); } public function SetState($state) { $this->state = $state; } public function GetState() { return $this->state; } } class SaveState { private $state; public function SetState($state) { $this->state = $state; } public function GetState() { return $this->state; } } class StateContainer { private $ss; public function SetSaveState($ss) { $this->ss = $ss; } public function GetSaveState() { return $this->ss; } } // 模擬短信發送 $mList = []; $scList = []; for ($i = 0; $i < 10; $i++) { $m = new Message('手機號' . $i, '內容' . $i); echo '初始狀態:'; $m->Show(); // 保存初始信息 $sc = new StateContainer(); $sc->SetSaveState($m->CreateSaveSate()); $scList[] = $sc; // 模擬短信發送,2發送成功,3發送失敗 $pushState = mt_rand(2, 3); $m->SetState($pushState == 2 ? '發送成功' : '發送失敗'); echo '發佈後狀態:'; $m->Show(); $mList[] = $m; } // 模擬另外一個線程查找發送失敗的並把它們還原到未發送狀態 sleep(2); foreach ($mList as $k => $m) { if ($m->GetState() == '發送失敗') { // 若是是發送失敗的,還原狀態 $m->SetSaveState($scList[$k]->GetSaveState()); } echo '查詢發佈失敗後狀態:'; $m->Show(); }
說明
關注公衆號:【硬核項目經理】獲取最新文章
添加微信/QQ好友:【xiaoyuezigonggong/149844827】免費得PHP、項目管理學習資料
知乎、公衆號、抖音、頭條搜索【硬核項目經理】
B站ID:482780532