GoF类图

备忘录模式

代码实现

php备忘PHP设计模式之备忘录模式 Node.js

classOriginator{private$state;publicfunctionSetMeneto(Memento$m){$this->state=$m->GetState();}publicfunctionCreateMemento(){$m=newMemento();$m->SetState($this->state);return$m;}publicfunctionSetState($state){$this->state=$state;}publicfunctionShowState(){echo$this->state,PHP_EOL;}}

原发器,也可以叫做发起人。
它有一个内部状态(state),这个状态可以在不同的情形下进行改变。
当某一个事宜发生时,须要将这个状态规复到原来的状态。
在这里,我们有一个CreateMemento()用于创建一个备忘录(存档),有一个SetMeneto()用于还原状态(读档)。

classMemento{private$state;publicfunctionSetState($state){$this->state=$state;}publicfunctionGetState(){return$this->state;}}

备忘录,非常大略,便是用于记录状态。
将这个状态以工具的形式保存,就可以让原发器非常方便地创建很多存档用于记录各种不同的状态。

classCaretaker{private$memento;publicfunctionSetMemento($memento){$this->memento=$memento;}publicfunctionGetMemento(){return$this->memento;}}

卖力人,也叫做管理者类,保存备忘录,当须要的时候从这里取出备忘录。
它只卖力保存,不能修正备忘录。
在繁芜的运用中,可以将这里做成列表,就像游戏中可以选择性的展现多条存档记录供玩家选择。

$o=newOriginator();$o->SetState('状态1');$o->ShowState();//保存状态$c=newCaretaker();$c->SetMemento($o->CreateMemento());$o->SetState('状态2');$o->ShowState();//还原状态$o->SetMeneto($c->GetMemento());$o->ShowState();

客户真个调用中,我们的原发器初始化状态后进行了保存,然后人为的变动了状态。
这时只须要通过卖力人将状态还原回来就可以了。

备忘录模式说白了便是让一个外部类B来保存A的内部状态,然后在适当的时候可以方便的还原这个状态。
备忘录模式的运用处景实在非常多,浏览器的回退、数据库的备份还原、操作系统的备份还原、文档的撤销重做、棋牌游戏的悔棋等等这个模式能够保持对原发器的封装,也便是这些状态须要对外部的工具隐蔽,以是只能交给一个备忘录工具来记录状态在原发器和备忘录之间的拷贝可能带来性能问题,特殊是大型工具的繁芜繁多的内部状态,而且也会带来一些编码方面的漏洞,比如漏掉某些状态

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

<?phpclassMessage{private$content;private$to;private$state;private$time;publicfunction__construct($to,$content){$this->to=$to;$this->content=$content;$this->state='未发送';$this->time=time();}publicfunctionShow(){echo$this->to,'---',$this->content,'---',$this->time,'---',$this->state,PHP_EOL;}publicfunctionCreateSaveSate(){$ss=newSaveState();$ss->SetState($this->state);return$ss;}publicfunctionSetSaveState($ss){if($this->state!=$ss->GetState()){$this->time=time();}$this->state=$ss->GetState();}publicfunctionSetState($state){$this->state=$state;}publicfunctionGetState(){return$this->state;}}classSaveState{private$state;publicfunctionSetState($state){$this->state=$state;}publicfunctionGetState(){return$this->state;}}classStateContainer{private$ss;publicfunctionSetSaveState($ss){$this->ss=$ss;}publicfunctionGetSaveState(){return$this->ss;}}//仿照短信发送$mList=[];$scList=[];for($i=0;$i<10;$i++){$m=newMessage('手机号'.$i,'内容'.$i);echo'初始状态:';$m->Show();//保存初始信息$sc=newStateContainer();$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($mListas$k=>$m){if($m->GetState()=='发送失落败'){//如果是发送失落败的,还原状态$m->SetSaveState($scList[$k]->GetSaveState());}echo'查询发布失落败后状态:';$m->Show();}

解释

短信类做为我们的原发器,在发送前就保存了当前的发送状态随机仿照短信发送,只有两个状态,发送成功或者失落败,并改变原发器的状态为成功或者失落败仿照另一个线程或者脚本对短信的发送状态进行检讨,如果创造有失落败的,就将它重新改回未发送的状态这里我们只是保存了发送状态这一个字段,其他原发器的内部属性并没有保存真实的场景下我们该当会有一个重试次数的限定,当超过这个次数后,状态改为彻底的发送失落败,不再进行重试了下期看点

备忘录模式便是这样我们平常每天都在用的模式,说是备忘,不如说是后悔模式更贴切些。
人生没有后悔药,但程序天下里可以有,还是那句话,养成备份主要文件、资料、代码的好习气,灵巧利用Git(不但是存储代码,比如这一系统文章)。
下回即将和我们见面的是桥接模式,不陌生吧,虚拟机上的网络配置就有桥接办法,那这货到底是干嘛的呢?且听下回分解。