18.3备忘录模式
备忘录(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
备忘录模式(Memento)结构图
Originator(发起人):负责创建一个备忘录 Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。Originator 可根据需要决定 Memento 存储 Originator 的哪些内部状态。
Memento(备忘录):负责存储 Originator 对象的内部状态,并可防止 Originator 以外的其他对象访问备忘录 Memento。备忘录有两个接口,Caretaker 只能看到备忘录的窄接口,它只能将备忘录传递给其他对象。Oirginator 能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。
Caretaker(管理者):负责保存好备忘录 Memento,不能对备忘录的内容进行操作或检查。
18.4备忘录模式基本代码
发起人(Originator)类
class Originator
{
//需要保存的属性,可能有多个
private $state = '';
public function setState($state)
{
$this->state = $state;
}
//创建备忘录,将当前需要保存的信息导入并
//实例化出一个 Memento 对象
public function CreateMemento()
{
return new Memento($this->state);
}
//恢复备忘录,将 Memento 导入并将
//相关数据恢复
public function SetMemento($memento)
{
$this->state = $memento->getState();
}
//显示数据
public function Show()
{
echo 'State = ' . $this->state . PHP_EOL;
}
}
备忘录(Memento)类
class Memento
{
private $state = '';
//构造方法,将相关数据导入
public function __construct($state)
{
$this->state = $state;
}
//需要保存的数据属性,可以是多个
public function getState()
{
return $this->state;
}
}
管理者(Caretaker)类
class Caretaker
{
private $memento = null;
//得到或设置备忘录
public function setMemento($memento)
{
$this->memento = $memento;
}
public function getMemento()
{
return $this->memento;
}
}
客户端代码
public function mementoDemo()
{
$o = new Originator();
//Originator 初始状态,状态属性为 “On”
$o->setState('On');
$o->Show();
//保存状态时,由于有了很好的封装
//可以隐藏 Originator 的实现细节
$c = new Caretaker();
$c->setMemento($o->CreateMemento());
//Originator 改变了状态属性为“Off”
$o->setState('Off');
$o->Show();
//恢复原初始状态
$o->SetMemento($c->getMemento());
$o->Show();
}
这当中就是把要保存的细节给封装在了 Memento 中了,哪一天要更改保存的细节也不用影响客户端了。
备忘录模式的使用场景:
- Memento 模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator 可以根据保存的 Memento 信息还原到前一状态。
- 命令模式也有实现类似撤销的作用,如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。
- 有时一些对象的内部信息必须保存在对象以外的地方,但是必须要由对象自己读取,这是,使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。
最大的作用还是在当角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
18.5游戏进度备忘
代码结构图 游戏角色类
class GameRole
{
private $vit;
private $atk;
private $def;
public function StateDisplay()
{
echo '角色当前状态:' . PHP_EOL;
echo '体力:' . $this->vit . PHP_EOL;
echo '攻击力:' . $this->atk . PHP_EOL;
echo '防御力:' . $this->def . PHP_EOL;
echo '================' . PHP_EOL;
}
//保存角色状态
public function SaveState()
{
return new RoleStateMemento($this->vit, $this->atk, $this->def);
}
//恢复角色状态
public function RecoverState($memento)
{
$this->vit = $memento->getVitality();
$this->atk = $memento->getAttack();
$this->def = $memento->getDefense();
}
public function GetInitState()
{
$this->vit = 100;
$this->atk = 100;
$this->def = 100;
}
public function Fight()
{
$this->vit = 0;
$this->atk = 0;
$this->def = 0;
}
}
角色状态存储箱类
class RoleStateMemento
{
private $vit;
private $atk;
private $def;
public function __construct($vit, $atk, $def)
{
$this->vit = $vit;
$this->atk = $atk;
$this->def = $def;
}
//生命力
public function getVitality()
{
return $this->vit;
}
//攻击力
public function getAttack()
{
return $this->atk;
}
//防御力
public function getDefense()
{
return $this->def;
}
}
角色状态管理者类
class RoleStateCaretaker
{
private $memento = null;
public function setMemento($memento)
{
$this->memento = $memento;
}
public function getMemento()
{
return $this->memento;
}
}
客户端代码
public function mementoImp()
{
//大战Boss前
$lixiaoyao = new GameRole();
$lixiaoyao->GetInitState();
$lixiaoyao->StateDisplay();
//保存进度
$stateAdmin = new RoleStateCaretaker();
$stateAdmin->setMemento($lixiaoyao->SaveState());
//大战Boss时,损耗严重
$lixiaoyao->Fight();
$lixiaoyao->StateDisplay();
//恢复之前状态
$lixiaoyao->RecoverState($stateAdmin->getMemento());
$lixiaoyao->StateDisplay();
}
备忘录模式的缺点:角色状态需要完整存储到备忘录对象中,如果状态数据很大很多,那么在资源消耗上,备忘录对象会非常耗内存。