备忘录,这个名字其实就已经很形象的解释了它的做用。典型的例子就是咱们原来玩硬盘游戏时的存档功能。当你对即将面对的大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也是相似的功能。咱们的手机操做系统上也决定开发这样的一个功能。当咱们点击时光机备份时,将手机上全部的资料、数据、状态信息都压缩保存起来,若是用户容许的话,咱们将这个压缩包上传到咱们的云服务器上避免占用用户的手机内存,不然就只能保存到用户的手机内存中了。当用户的手机须要恢复到某个时间点,咱们将全部的时光机备份列出,用户只须要用手指轻轻一按就能够把手机系统状态恢复到当时的样子了,是否是很是方便!!服务器
完整代码:github.com/zhangyue050…网络
此次又回到短信发送的例子上来。一般咱们作短信或者邮件发送这些功能时,会有一个队列从数据库或者缓存中读取要发送的内容进行发送,若是成功了就无论了,若是失败了会将短信的状态改为失败或者重发。在这里,咱们直接将它改回到以前未发送的状态而后等待下次发送的队列再次执行发送。
短信发送类图
<?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();
}
复制代码
说明
备忘录模式就是这样咱们日常每天都在用的模式,说是备忘,不如说是后悔模式更贴切些。人生没有后悔药,但程序世界里能够有,仍是那句话,养成备份重要文件、资料、代码的好习惯,灵活使用Git(不仅是存储代码,好比这一系统文章)。下回即将和咱们见面的是桥接模式,不陌生吧,虚拟机上的网络配置就有桥接方式,那这货究竟是干吗的呢?且听下回分解。