Memento_备忘录模式定义:
在不破坏封装性的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
一个备忘录是一个对象,它储存一个对象在某个瞬间的内部状态,后者被称为备忘录的原发器。
从定义看,备忘录模式最重要的功能就是记录某个对象某个瞬间的状态,作为备忘之用。而我们获取了这个备份数据有什么用呐,其实就是为了恢复到那个瞬间的状态,虽然不是一定会恢复,但是目的就是为了恢复。这也是很多人理解备忘录模式的时候,忽略掉的地方,他们太关注备忘,而忽略了恢复,这样是不全面的理解。
应用Memento_备忘录模式解决问题的思路:
在写命令模式的时候,写了一个简单的计算器的实例。现在我们要回想一下,当时也完成了撤销及恢复命令的功能。但那时候的实现是基于对应的命令操作来实现的,即加法的对应是减法,减法对应的就是加法,互操作即可完成功能。现在用备忘录模式来解决这个问题试试,因为备忘录模式最大的用处就是获取某个点的状态,并回复。下面是一个命令模式和备忘录模式混合使用的实例,用命令模式来实现具体各种算法的操作,使用备忘录模式来完成撤销及恢复的功能。
<?php
/**
* Memento_备忘录模式_PHP语言描述
*/
interface Memento{
}
/**
* 定义一个命令的接口
*/
interface Command{
/**
* 执行命令
*/
public function execute();
/**
* 撤销命令,恢复到备忘录对象记录的状态
* @param memento 备忘录对象
*/
public function undo($memento);
/**
* 重做命令,恢复到备忘录对象记录的状态
* @param memento 备忘录对象
*/
public function redo($memento);
/**
* 创建保存原发器对象状态的备忘录独享
* @return 创建好的备忘录对象
*/
public function createMemento();
}
/**
* 操作运算的接口
*/
interface OperationApi{
/**
* 获取计算完成后的结果
* @return 计算完成后的结果
*/
public function getResult();
/**
* 执行加法
* @param num 需要加的数
*/
public function add($num);
/**
* 执行减法
* @param num 需要减的数
*/
public function substract($num);
/**
* 创建保存原发器对象状态的备忘录对象
* @return 创建好的备忘录对象
*/
public function createMemento();
/**
* 重新设置原发器对象状态,让其会到备忘录对象记录的状态
* @param memento 记录有原发器状态的备忘录对象
*/
public function setMemento($memento);
}
/**
* 命令对象的公共对象,实现各个命令对象的公共方法
*/
class AbstractCommand implements Command{
/**
* 具体的功能实现,这里不需要实现
*/
public function execute(){
}
/**
* 持有真正的命令实现者对象
*/
protected $operation = null;
public function setOperation($operation){
$this->operation = $operation;
}
public function createMemento(){
return $this->operation->createMemento();
}
public function redo($memento){
$this->operation->setMemento($memento);
}
public function undo($memento){
$this->operation->setMemento($memento);
}
}
/**
* 具体的加法命令类实现,由于继承于公共的命令类,只要实现与自己相关的部分即可
*/
class AddCommand extends AbstractCommand{
private $opeNum;
public function __construct($opeNum){
$this->opeNum = $opeNum;
}
public function execute(){
$this->operation->add($this->opeNum);
}
}
/**
* 具体的减法命令类实现,由于继承于公共的命令类,只要实现与自己相关的部分即可
*/
class SubstractCommand extends AbstractCommand{
private $opeNum;
public function __construct($opeNum){
$this->opeNum = $opeNum;
}
public function execute(){
$this->operation->substract($this->opeNum);
}
}
/**
* 运算类,真正实现加减法运算
*/
class Operation implements OperationApi{
/**
* 记录运算的结果
*/
private $result;
public function getResult(){
return $this->result;
}
public function add($num){
$this->result += $num;
}
public function substract($num){
$this->result -= $num;
}
public function createMemento(){
return $m = new MementoImpl($this->result);
}
public function setMemento($memento){
$this->result = $memento->getResult();
}
}
class MementoImpl implements Memento{
private $result = 0;
public function __construct($result){
$this->result = $result;
}
public function getResult(){
return $this->result;
}
}
/**
* 计算器类,计算器上有加法按钮,减法按钮,还有撤销和恢复的按钮
*/
class Calculator{
/**
* 命令的操作历史记录,在撤销时用
*/
private $undoCmds = array();
/**
* 命令被撤销的历史记录,在恢复时用
*/
private $redoCmds = array();
/**
* 命令操作对应的备忘录对象的历史记录,在撤销时用
* 数组有两个元素,第一个是命令执行前的状态,第二个是命令执行后的状态
*/
private $undoMementos = array();
/**
* 被撤销命令操作对应的备忘录对象的历史记录,在撤销时用
* 数组有两个元素,第一个是命令执行前的状态,第二个是命令执行后的状态
*/
private $redoMementos = array();
private $addCmd = null;
private $substractCmd = null;
public function setAddCmd($addCmd){
$this->addCmd = $addCmd;
}
public function setSubstractCmd($substractCmd){
$this->substractCmd = $substractCmd;
}
public function addPressed(){
//获取对应的备忘录对象,并保存在相应的历史记录中
$m1 = $this->addCmd->createMemento();
//执行命令
$this->addCmd->execute();
//把操作记录到历史记录中
array_push($this->undoCmds, $this->addCmd);
//获取执行命令后的备忘录对象
$m2 = $this->addCmd->createMemento();
//设置到撤销的历史记录中
array_push($this->undoMementos,array($m1,$m2));
}
public function substractPressed(){
//获取对应的备忘录对象,并保存在相应的历史记录中
$m1 = $this->substractCmd->createMemento();
//执行命令
$this->substractCmd->execute();
//把操作记录到历史记录中
array_push($this->undoCmds,$this->substractCmd);
//获取执行命令后的备忘录对象
$m2 = $this->substractCmd->createMemento();
//设置到撤销的历史记录中
array_push($this->undoMementos,array($m1,$m2));
}
public function undoPressed(){
if(count($this->undoCmds) > 0){
//取出最后一个命令来撤销
$cmd = $this->undoCmds[count($this->undoCmds) - 1];
//获取对应的备忘录对象
$ms = $this->undoMementos[count($this->undoCmds) - 1];
//撤销
$cmd->undo($ms[0]);
//如果还有恢复的功能,那就把这个命令记录到恢复的历史记录中
array_push($this->redoCmds,$cmd);
//把相应的备忘录对象也添加过去
array_push($this->redoMementos,$ms);
//然后把最后一个命令删除
array_pop($this->undoCmds);
//把相应的备忘录对象也删除
array_pop($this->undoMementos);
}
else {
echo "已经没有可撤销的命令了";
}
}
public function redoPressed(){
if(count($this->redoCmds) > 0){
//取出最后一个命令来重做
$cmd = $this->redoCmds[count($this->redoCmds) - 1];
//取出相应的备忘录对象
$ms = $this->redoMementos[count($this->redoCmds) - 1];
//重做
$cmd->redo($ms[1]);
//把这个命令记录到可撤销的历史记录中
array_push($this->undoCmds,$cmd);
//把相应的备忘录对象也添加过去
array_push($this->undoMementos,$ms);
//然后把最后一个命令删除
array_pop($this->redoCmds);
array_pop($this->redoMementos);
}
else{
echo "已经没有可恢复的命令了";
}
}
}
//1.组装命令和接收者
//创建接收者
$operation = new Operation();
//创建命令
$addCmd = new AddCommand(5);
$substractCmd = new SubstractCommand(3);
//组装命令和接收者
$addCmd->setOperation($operation);
$substractCmd->setOperation($operation);
//2.把命令设置到持有者,就是计算器中
$calculator = new Calculator();
$calculator->setAddCmd($addCmd);
$calculator->setSubstractCmd($substractCmd);
//3.模拟按下按钮,测试一下
$calculator->addPressed();
echo "<br>一次加法运算后的结果为:".$operation->getResult();
$calculator->addPressed();
echo "<br>一次加法运算后的结果为:".$operation->getResult();
$calculator->addPressed();
echo "<br>一次加法运算后的结果为:".$operation->getResult();
$calculator->substractPressed();
echo "<br>一次减法运算后的结果为:".$operation->getResult();
$calculator->substractPressed();
echo "<br>一次减法运算后的结果为:".$operation->getResult();
//测试撤销
$calculator->undoPressed();
echo "<br>一次撤销运算后的结果为:".$operation->getResult();
$calculator->undoPressed();
echo "<br>一次撤销运算后的结果为:".$operation->getResult();
//测试恢复
$calculator->redoPressed();
echo "<br>一次恢复运算后的结果为:".$operation->getResult();
$calculator->redoPressed();
echo "<br>一次恢复运算后的结果为:".$operation->getResult();
?>