18.3备忘录模式

备忘录(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
备忘录模式(Memento)结构图 第十八章 如果再回到从前——备忘录模式 - 图1Originator(发起人):负责创建一个备忘录 Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。Originator 可根据需要决定 Memento 存储 Originator 的哪些内部状态。
Memento(备忘录):负责存储 Originator 对象的内部状态,并可防止 Originator 以外的其他对象访问备忘录 Memento。备忘录有两个接口,Caretaker 只能看到备忘录的窄接口,它只能将备忘录传递给其他对象。Oirginator 能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。
Caretaker(管理者):负责保存好备忘录 Memento,不能对备忘录的内容进行操作或检查。

18.4备忘录模式基本代码

发起人(Originator)类

  1. class Originator
  2. {
  3. //需要保存的属性,可能有多个
  4. private $state = '';
  5. public function setState($state)
  6. {
  7. $this->state = $state;
  8. }
  9. //创建备忘录,将当前需要保存的信息导入并
  10. //实例化出一个 Memento 对象
  11. public function CreateMemento()
  12. {
  13. return new Memento($this->state);
  14. }
  15. //恢复备忘录,将 Memento 导入并将
  16. //相关数据恢复
  17. public function SetMemento($memento)
  18. {
  19. $this->state = $memento->getState();
  20. }
  21. //显示数据
  22. public function Show()
  23. {
  24. echo 'State = ' . $this->state . PHP_EOL;
  25. }
  26. }

备忘录(Memento)类

  1. class Memento
  2. {
  3. private $state = '';
  4. //构造方法,将相关数据导入
  5. public function __construct($state)
  6. {
  7. $this->state = $state;
  8. }
  9. //需要保存的数据属性,可以是多个
  10. public function getState()
  11. {
  12. return $this->state;
  13. }
  14. }

管理者(Caretaker)类

  1. class Caretaker
  2. {
  3. private $memento = null;
  4. //得到或设置备忘录
  5. public function setMemento($memento)
  6. {
  7. $this->memento = $memento;
  8. }
  9. public function getMemento()
  10. {
  11. return $this->memento;
  12. }
  13. }

客户端代码

  1. public function mementoDemo()
  2. {
  3. $o = new Originator();
  4. //Originator 初始状态,状态属性为 “On”
  5. $o->setState('On');
  6. $o->Show();
  7. //保存状态时,由于有了很好的封装
  8. //可以隐藏 Originator 的实现细节
  9. $c = new Caretaker();
  10. $c->setMemento($o->CreateMemento());
  11. //Originator 改变了状态属性为“Off”
  12. $o->setState('Off');
  13. $o->Show();
  14. //恢复原初始状态
  15. $o->SetMemento($c->getMemento());
  16. $o->Show();
  17. }

这当中就是把要保存的细节给封装在了 Memento 中了,哪一天要更改保存的细节也不用影响客户端了。
备忘录模式的使用场景:

  • Memento 模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator 可以根据保存的 Memento 信息还原到前一状态。
  • 命令模式也有实现类似撤销的作用,如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。
  • 有时一些对象的内部信息必须保存在对象以外的地方,但是必须要由对象自己读取,这是,使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。
  • 最大的作用还是在当角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。

    18.5游戏进度备忘

    代码结构图 第十八章 如果再回到从前——备忘录模式 - 图2游戏角色类

    1. class GameRole
    2. {
    3. private $vit;
    4. private $atk;
    5. private $def;
    6. public function StateDisplay()
    7. {
    8. echo '角色当前状态:' . PHP_EOL;
    9. echo '体力:' . $this->vit . PHP_EOL;
    10. echo '攻击力:' . $this->atk . PHP_EOL;
    11. echo '防御力:' . $this->def . PHP_EOL;
    12. echo '================' . PHP_EOL;
    13. }
    14. //保存角色状态
    15. public function SaveState()
    16. {
    17. return new RoleStateMemento($this->vit, $this->atk, $this->def);
    18. }
    19. //恢复角色状态
    20. public function RecoverState($memento)
    21. {
    22. $this->vit = $memento->getVitality();
    23. $this->atk = $memento->getAttack();
    24. $this->def = $memento->getDefense();
    25. }
    26. public function GetInitState()
    27. {
    28. $this->vit = 100;
    29. $this->atk = 100;
    30. $this->def = 100;
    31. }
    32. public function Fight()
    33. {
    34. $this->vit = 0;
    35. $this->atk = 0;
    36. $this->def = 0;
    37. }
    38. }

    角色状态存储箱类

    1. class RoleStateMemento
    2. {
    3. private $vit;
    4. private $atk;
    5. private $def;
    6. public function __construct($vit, $atk, $def)
    7. {
    8. $this->vit = $vit;
    9. $this->atk = $atk;
    10. $this->def = $def;
    11. }
    12. //生命力
    13. public function getVitality()
    14. {
    15. return $this->vit;
    16. }
    17. //攻击力
    18. public function getAttack()
    19. {
    20. return $this->atk;
    21. }
    22. //防御力
    23. public function getDefense()
    24. {
    25. return $this->def;
    26. }
    27. }

    角色状态管理者类

    1. class RoleStateCaretaker
    2. {
    3. private $memento = null;
    4. public function setMemento($memento)
    5. {
    6. $this->memento = $memento;
    7. }
    8. public function getMemento()
    9. {
    10. return $this->memento;
    11. }
    12. }

    客户端代码

    1. public function mementoImp()
    2. {
    3. //大战Boss前
    4. $lixiaoyao = new GameRole();
    5. $lixiaoyao->GetInitState();
    6. $lixiaoyao->StateDisplay();
    7. //保存进度
    8. $stateAdmin = new RoleStateCaretaker();
    9. $stateAdmin->setMemento($lixiaoyao->SaveState());
    10. //大战Boss时,损耗严重
    11. $lixiaoyao->Fight();
    12. $lixiaoyao->StateDisplay();
    13. //恢复之前状态
    14. $lixiaoyao->RecoverState($stateAdmin->getMemento());
    15. $lixiaoyao->StateDisplay();
    16. }

    备忘录模式的缺点:角色状态需要完整存储到备忘录对象中,如果状态数据很大很多,那么在资源消耗上,备忘录对象会非常耗内存。