面向对象编程思想-备忘录模式
一、引言
上篇博文中我们分享了访问者模式,访问者模式是把作用于数据结构上的操作封装到访问者类中,使得数据结构与操作分离。今天我们要学习的备忘录模式与命令模式有点相似,不同的是,命令模式保存的是发起人的具体命令(命令对应行为),而备忘录模式保存的是发起人的状态(状态对应数据内部结构,如属性)。下面请看今天要学习的访问者模式
二、备忘录模式
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
下面是备忘录模式的结构图:
下面是备忘录模式代码demo:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//发起人 class Originator { private string state; public string State { get { return state; } set { state = value; } } //通过实例化备忘录 保存状态 public Memento CreateMemento() { return new Memento(state); } //将备忘录中状态恢复给发起人 public void SetMemento(Memento memento) { this.state = memento.State; } public void Show() { Console.WriteLine($"当前state为{State}"); } } //备忘录类 class Memento { private string state; public Memento(string state) { this.state = state; } public string State { get { return state; } } } //管理者类,负责保存备忘录 class Caretaker { private Memento memento; public Memento Memento { get { return memento; } set { memento = value; } } } class Program { static void Main(string[] args) { //创建发起人 (修改状态前) Originator originator = new Originator(); originator.State = "我现在感觉很充实"; originator.Show(); //保存状态 由于封装在Memento中,因此客户端并不知道保存了哪些具体的发起人数据 Caretaker caretaker = new Caretaker(); caretaker.Memento = originator.CreateMemento(); //修改状态 originator.State = "稍微有一点点的饿"; originator.Show(); //恢复状态 originator.SetMemento(caretaker.Memento); originator.Show(); Console.Read(); } }
分析:把保存状态的细节封装到Memento中,这样哪天更改保存的细节也不会影响客户端。使用备忘录模式可以将复杂对象的内部信息对其他对象屏蔽起来。当角色状态改变时,有可能这个状态无效,可以使用暂时存储的备忘录将状态复原。
下面是多状态多备份备忘录实例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//游戏进度类 class GameState { //生命力 public string Vitality { get; set; } //攻击力 public string Attackpower { get; set; } //防御力 public string Defense { get; set; } } //游戏发起人 class GamePlayer { public List<GameState> LstGameState { get; set; } public GamePlayer(List<GameState> lstGameState) { this.LstGameState = lstGameState; } //创建备忘录,将要保存的游戏进度列表导入备忘录 public GameMemento CreateGameMemento() { return new GameMemento(new List<GameState>(this.LstGameState)); } //将备忘录中数据备份导入游戏进度列表 public void SetGameMemento(GameMemento gameMemento) { if (gameMemento != null) { this.LstGameState = gameMemento.LstGameStateBak; } } public void Show() { Console.WriteLine($"游戏保存了{LstGameState.Count}个进度,分别是"); foreach (GameState gameState in LstGameState) { Console.WriteLine($"生命力{gameState.Vitality},攻击力{gameState.Attackpower},防御力{gameState.Defense}"); } } } //备忘录 class GameMemento { public List<GameState> LstGameStateBak { get; set; } public GameMemento(List<GameState> lstGameState) { this.LstGameStateBak = lstGameState; } } class GameCaretaker { //使用多个备忘录来储存备份点 public Dictionary<string,GameMemento> DicGameMemento { get; set; } public GameCaretaker() { DicGameMemento = new Dictionary<string, GameMemento>(); } } class Program { static void Main(string[] args) { List<GameState> lstGameState = new List<GameState>() { new GameState(){Vitality="100",Attackpower="100",Defense="100"}, new GameState(){Vitality="80",Attackpower="80",Defense="80"}, new GameState(){Vitality="50",Attackpower="50",Defense="50"} }; GamePlayer gamePlayer = new GamePlayer(lstGameState); gamePlayer.Show(); //创建备忘录并保存进度 GameCaretaker gameCaretaker = new GameCaretaker(); gameCaretaker.DicGameMemento.Add(DateTime.Now.ToString(),gamePlayer.CreateGameMemento()); //移除最后一个游戏进度 gamePlayer.LstGameState.RemoveAt(2); gamePlayer.Show(); Thread.Sleep(1000); //第二次备份 gameCaretaker.DicGameMemento.Add(DateTime.Now.ToString(), gamePlayer.CreateGameMemento()); //恢复到指定进度 var keyCollection = gameCaretaker.DicGameMemento.Keys; foreach (string k in keyCollection) { Console.WriteLine($"key={k}"); } while (true) { Console.WriteLine("请输入数字,按关闭键退出"); int index = -1; try { index = Int32.Parse(Console.ReadLine()); } catch (Exception) { Console.WriteLine("输入的字符格式不正确"); continue; } GameMemento gameMemento = null; if(index>-1&& index< gamePlayer.LstGameState.Count && gameCaretaker.DicGameMemento.TryGetValue(keyCollection.ElementAt(index),out gameMemento)) { gamePlayer.SetGameMemento(gameMemento); gamePlayer.Show(); } else { Console.WriteLine("索引超出界限"); } } } }
分析:实际应用中,大多情况下用到的是多状态多备份,如果状态很大很多,备忘录对象会很耗内存
优点:
1.如果某个操作错误破坏了数据的完整性,可以使用备忘录模式恢复原来保存的数据
2.备份的状态保存在发起人之外,这样发起人就不需要对各个保存的状态进行管理。
缺点:
1.在实际系统中,可能需要多状态多备份,对系统资源消耗是比较大的
适用场景:
1.如果系统需要提供回滚操作时,使用备忘录模式是比较方便的。例如数据库中事务操作,文本编辑器中的Ctrl+Z撤销操作
参考:
大话设计模式
http://www.cnblogs.com/zhili/p/MementoPattern.html;
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。