备忘录模式(Memento Pattern)

该文章的最新版本已迁移至个人博客【比特飞】,单击链接 https://www.byteflying.com/archives/421 访问。

备忘录模式属于行为型模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

备忘录模式为我们提供了“后悔药”的机制,为我们在需要的时候,可以将对对象的修改撤销甚至重做。

角色:

1、原发器(Originator)

创建一个备忘录,用以记录当前时刻它的内部状态,在需要时使用备忘录恢复内部状态;

2、备忘录(Memento)

将原发器对象的内部状态存储起来;

3、备忘录管理者(Caretaker)

负责保存好备忘录,不能对备忘录的内容进行操作或检查。

示例:

命名空间MementoPattern中包含Memento备忘录类,Caretaker管理者类,象棋Chessman类。本案例将向大家演示如何撤销或重做对象棋位置的修改操作。本案例使用栈以支持多次撤销,并且重做支持前1次的多次撤销。本案例不支持重新设置棋子位置时所产生的分支。

namespace MementoPattern
public partial class Chessman {private Point _position;private Caretaker _caretaker = null;public Point Position {get => _position;set {_position = value;_caretaker.Memento.Position = value; Console.WriteLine(String.Format(Const.POSITION_INFO, _position.X, _position.Y));}}public Chessman() : this(new Point(0, 0)) {}public Chessman(Point point) {_caretaker = new Caretaker(new Memento());Position = point;}}

象棋棋子类Chessman,内部维持棋子的位置,在设置棋子位置时将信息保存到管理者所管理的备忘录中。

public partial class Chessman {public Chessman Undo(int step) {try {Console.WriteLine(Const.ARROW_LEFT);Console.WriteLine($"Undo({step})!");this._position = _caretaker.Memento.Undo(step);Console.WriteLine(String.Format(Const.POSITION_INFO, _position.X, _position.Y));Console.WriteLine(Const.ARROW_RIGHT);return this;} catch(Exception ex) {Console.WriteLine(ex.Message);Console.WriteLine(Const.ARROW_RIGHT);return this;}}public Chessman Redo() {try {Console.WriteLine(Const.ARROW_LEFT);Console.WriteLine("Redo()!");this._position = _caretaker.Memento.Redo();Console.WriteLine(String.Format(Const.POSITION_INFO, _position.X, _position.Y));Console.WriteLine(Const.ARROW_RIGHT);return this;} catch(Exception ex) {Console.WriteLine(ex.Message);Console.WriteLine(Const.ARROW_RIGHT);return this;}}}

象棋棋子类Chessman的第2部分(partial),支持按步数撤销位置,并且支持重做命令。

public partial class Memento {private Point _position;public Point Position {get => _position;set {_position = value;_history.Push(new RedoInfo { Position = value });_redoList.Clear();}}public Memento() {_history = new Stack<RedoInfo>();_redoList = new Stack<RedoInfo>();}private Stack<RedoInfo> _history = null;private Stack<RedoInfo> _redoList = null;public Point Undo(int step) {int totalCount = 0;List<string> temp = new List<string>();foreach(var item in _history) {if(string.IsNullOrWhiteSpace(item.GUID)) {totalCount++;} else {if(!temp.Contains(item.GUID)) {totalCount++;temp.Add(item.GUID);}}}if(step >= totalCount) {throw new InvalidOperationException("Too much steps!");}var guid = Guid.NewGuid().ToString("B");for(int i = 1; i <= step; i++) {Undo(guid);}return _position;}}

备忘录类Memento,内部维持对位置的引用并用2个栈分别管理历史和重做数据。

public partial class Memento {private void UndoLoop(string guid) {var history = _history.Pop();history.GUID = guid;_redoList.Push(history);_position = _history.Peek().Position;}private void Undo(string guid) {var temp = _history.Peek().GUID;if(string.IsNullOrWhiteSpace(temp)) {UndoLoop(guid);} else {while(_history.Peek().GUID == temp) {UndoLoop(guid);}}}public Point Redo() {if(_redoList.Count == 0) {throw new InvalidOperationException("You can not redo now!");}var guid = _redoList.Peek().GUID;while(_redoList.Count != 0 && _redoList.Peek().GUID == guid) {_history.Push(_redoList.Pop());_position = _history.Peek().Position;}return _position;}}

备忘录类Memento的第2部分(partial),包含了按步数撤销和重做的具体逻辑。

public class Caretaker {public Memento Memento { get; set; }public Caretaker(Memento memento) {Memento = memento;}}

管理者Caretaker类,管理者负责维持备忘录。

public class RedoInfo {public Point Position { get; set; }public string GUID { get; set; }}

重做RedoInfo类,按GUID的值判断是否是同一“批次”被撤销的。

public class Const {public const string POSITION_INFO = "Current position is ({0},{1})!";public const string ARROW_LEFT = "<---------------------------";public const string ARROW_RIGHT = "--------------------------->";}

常量类Const,维护一些本案例中经常用到的字符串。在实际开发过程中不应当有此类,应该将相应的常量放在具体要使用的类中。2017年,阿里发布《阿里巴巴Java开发手册》,其中有一节提到此准则,所有使用面向对象编程语言的开发人员都应当遵从。

public class Program {private static Chessman _chessman = null;public static void Main(string[] args) {_chessman = new Chessman(new Point(1, 10));_chessman.Position = new Point(2, 20);_chessman.Position = new Point(3, 30);_chessman.Position = new Point(4, 40);_chessman.Position = new Point(5, 50);_chessman.Position = new Point(9, 40);_chessman.Undo(1).Undo(2).Undo(1).Redo().Redo().Redo().Redo().Redo().Undo(6).Undo(5).Undo(4);Console.ReadKey();}}

以上是调用方的代码演示,撤销和重做方法经过特殊处理以支持方法链。以下是这个案例的输出结果:

Current position is (1,10)!
Current position is (2,20)!
Current position is (3,30)!
Current position is (4,40)!
Current position is (5,50)!
Current position is (9,40)!
<---------------------------
Undo(1)!
Current position is (5,50)!
--------------------------->
<---------------------------
Undo(2)!
Current position is (3,30)!
--------------------------->
<---------------------------
Undo(1)!
Current position is (2,20)!
--------------------------->
<---------------------------
Redo()!
Current position is (3,30)!
--------------------------->
<---------------------------
Redo()!
Current position is (5,50)!
--------------------------->
<---------------------------
Redo()!
Current position is (9,40)!
--------------------------->
<---------------------------
Redo()!
You can not redo now!
--------------------------->
<---------------------------
Redo()!
You can not redo now!
--------------------------->
<---------------------------
Undo(6)!
Too much steps!
--------------------------->
<---------------------------
Undo(5)!
Too much steps!
--------------------------->
<---------------------------
Undo(4)!
Current position is (1,10)!
--------------------------->

优点:

该文章的最新版本已迁移至个人博客【比特飞】,单击链接 https://www.byteflying.com/archives/421 访问。

1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态;
2、实现了信息的封装,使得用户不需要关心状态的保存细节。

缺点:

1、如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

使用场景:

1、需要保存/恢复数据的相关状态场景;
2、提供一个可回滚的操作。

C#设计模式之18-备忘录模式相关推荐

  1. IOS设计模式之四(备忘录模式,命令模式)

    本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq) ...

  2. 设计模式读书笔记-----备忘录模式

    个人比较喜欢玩单机游戏,什么仙剑.古剑.鬼泣.使命召唤.三国无双等等一系列的游戏我都玩过(现在期待凡人修仙传),对于这些游戏除了剧情好.场面大.爽快之外,还可以随时存档,等到下次想玩了又可以从刚开始的 ...

  3. Java描述设计模式(24):备忘录模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 1.场景描述 常见的视频播放软件都具备这样一个功能:假设在播放视频西游记,如果这时候切换播放视频红楼梦,当再次切回播放西游记时, ...

  4. java备忘录模式应用场景_Java描述设计模式(24):备忘录模式

    一.生活场景 1.场景描述 常见的视频播放软件都具备这样一个功能:假设在播放视频西游记,如果这时候切换播放视频红楼梦,当再次切回播放西游记时,视频会从上次切走的时间点继续播放.下面基于备忘录设计模式来 ...

  5. 一天一种设计模式之二-----备忘录模式

    2019独角兽企业重金招聘Python工程师标准>>> 一.备忘录模式 备忘录模式属于三种设计模式中的行为型模式(另外两种是创建型模式和结构型模式). 定义:在不破坏封闭性的前提下, ...

  6. java备忘录代码和解释_Java描述设计模式(24):备忘录模式

    一.生活场景 1.场景描述 常见的视频播放软件都具备这样一个功能:假设在播放视频西游记,如果这时候切换播放视频红楼梦,当再次切回播放西游记时,视频会从上次切走的时间点继续播放.下面基于备忘录设计模式来 ...

  7. 《研磨设计模式》chap19 备忘录模式

    应用场景:需要保存对象的状态和值. 1. 简介 public interface Memento { }public class Originator {//表示原发器的状态 private Stri ...

  8. Net设计模式实例之备忘录模式(Memento Pattern)

    一.备忘录模式简介(Brief Introduction) 备忘录模式(Memento Pattern),在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可以就该对 ...

  9. [设计模式-行为型]备忘录模式(Memento)

    一句话 让某个对象可以取到之前的某个状态值. 概括 解析 MEMENTO-同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么 ...

  10. 设计模式笔记(21)---备忘录模式(行为型)

    Gof定义 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可以将该对象恢复到原先保存的状态. 动机 在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需 ...

最新文章

  1. zuul 动态路由mysql_zuul 动态路由 - typistw的个人空间 - OSCHINA - 中文开源技术交流社区...
  2. 训练softmax分类器实例_知识蒸馏:如何用一个神经网络训练另一个神经网络
  3. HDU 2852 KiKi's K-Number 主席树
  4. MULE ESB功能介绍
  5. 判断扫码是支付宝还是微信
  6. .net post提交后接收返回数据_Ajax提交表单的方式
  7. 运用java 多线程模拟火车售票。。。。
  8. 谷歌爬虫ua_Python爬虫,实战爬取腾讯视频评论!
  9. A - 敌兵布阵(线段树)(单点更新区间求和)
  10. 6个提高工作效率的技巧
  11. html中橘色代码,javascript HTML+CSS实现经典橙色导航菜单
  12. 笃行杂记之Zookeeper SessionTimeOut分析
  13. Nagios学习笔记
  14. 七种功能强大的聊天机器人平台
  15. CCF NOI 1041.志愿者选拔
  16. 宽带拨号上网连接错误720
  17. WIN2000服务器安全配置(一)(转)
  18. 与外包人员合作时,如何管理代码?
  19. 基于51单片机液位检测与报警
  20. NGS数据分析实践:03. 涉及的常用数据格式[2] - sam/bam格式

热门文章

  1. mysql服务的关闭与启动
  2. c#winform演练 ktv项目 制作歌曲播放列表
  3. 对象交互 空调与摇控器 0107
  4. TreeView控件 1202
  5. python-面向对向-实例方法的继承
  6. 设置centos上的redis可以被访问
  7. linux-关于用户的约定
  8. Elasticsearch 数据写入原理
  9. 【GO】golang 的new 和 make
  10. Linux系统可卸载内核模块完全指南(上)