设计模式----备忘录模式
前面说的设计模式几乎都有用到继承、接口,今天,我要说的这个设计模式,它就没有用到任何接口,也不需要抽象类,更没有复杂的继承关系,它就是备忘录模式,何为备忘录,按照现实世界的理解,备忘录就是人们在此刻记录一些东西用作未来供我们回忆想起此刻所记录的信息,大白话就是记录作用,我们今天的备忘录也是一样的道理。
备忘录,首先当然是备忘录类了,请看代码
class Memento { private string state; public Memento(string state) { this.state = state; } public string State { get { return state; } } }
这里就是一个构造函数注入和一个属性,因为我们是简化版,所以只用一个属性来替代,所谓备忘录类,就是用来保存,这里的state字段就是我们要保存的值,所以为什么State这个属性才只有get方法而没有set方法了。备忘录有了,肯定要有管理备忘录的对象啊。请看下面的代码
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) { state = memento.State; } public void Show() { Console.WriteLine("State=" + state); } }
这个对象最重要的方法就是CreateMemento方法和SetMemento方法,他们分别是创建备忘录对象保存当前状态和根据备忘录状态来恢复当前状态,看代码已经是很明显了。。。,看到这里你是否觉得哪里还少点东西呢??备忘录对象如何保存呢??没有备忘录对象又怎么恢复呢。。所以,接下来,我们就要创建一个保存备忘录的对象,请看代码。。
class Caretaker { private Memento memento; public Memento Memento { get { return memento; } set { memento = value; } } }
这个对象很简单,就是一个属性,这个就是用来保存备忘录对象的对象,是不是很简单,这就是所有的备忘录模式。好了,照老规矩,我们还是跑一遍演示一下是如何保存恢复备忘录模式的
class Program { static void Main(string[] args) { Originator o = new Originator(); o.State = "ON"; o.Show(); Caretaker c = new Caretaker(); c.Memento = o.CreateMemento(); o.State = "Off"; o.Show(); o.SetMemento(c.Memento); o.Show(); Console.ReadLine(); } }
首先我们创建了一个Originator对象,我们设置它的初始状态是ON,然后显示,接下来我们创建Caretaker对象来保存初始状态为ON的Memento对象,然后我们改变初始状态为Off,显示现在改变的状态,最后我们通过我们保存的备忘录对象来恢复到之前的状态然后显示出来。这就是完整的备忘录模式,我们可以看看运行结果
第一次显示状态是On,接下来是Off,最后还原成On。。。
上面的那些是设计模式的框架,下面,让我们用这个框架来实现我们今天的场景:游戏存档。
首先,我们根据上面的框架来设计我们的类型,首先是存档类(备忘录类),游戏存档,当然是存游戏角色的状态,请看代码
/// <summary> /// 角色状态存储箱 /// </summary> class RoleStateMemento { private int vit; private int atk; private int def; public RoleStateMemento(int vit, int atk, int def) { this.vit = vit; this.atk = atk; this.def = def; } /// <summary> /// 生命力 /// </summary> public int Vitality { get { return vit; } } /// <summary> /// 攻击力 /// </summary> public int Atthack { get { return atk; } } /// <summary> /// 防御力 /// </summary> public int Defense { get { return def; } } }
是不是有种似曾相识的感觉呢,如果没有的话请看上面的Memento类,这里我们的存档类(备忘录)有三个字段,分别是生命力、攻击力和防御力,好了,下一步就是人物类(Originator)了
class GameRole { private int vit; /// <summary> /// 生命力 /// </summary> public int Vit { get { return vit; } set { vit = value; } } private int atk; /// <summary> /// 攻击力 /// </summary> public int Attack { get { return atk; } set { atk = value; } } private int def; /// <summary> /// 防御力 /// </summary> public int Defense { get { return def; } set { def = value; } } /// <summary> /// 状态显示 /// </summary> public void StateDisplay() { Console.WriteLine("角色当前状态:"); Console.WriteLine("体力:{0}", vit); Console.WriteLine("攻击力:{0}", atk); Console.WriteLine("防御力:{0}", def); Console.WriteLine(""); } /// <summary> /// 获得初始状态 /// </summary> public void GetInitState() { this.vit = 100; this.atk = 100; this.def = 100; } /// <summary> /// 战斗 /// </summary> public void Fight() { this.vit = 50; this.def = 50; this.atk = 50; } /// <summary> /// 保存角色状态 /// </summary> /// <returns></returns> public RoleStateMemento SaveState() { return (new RoleStateMemento(vit, atk, def)); } //恢复角色状态 public void RecoveryState(RoleStateMemento memento) { this.vit = memento.Vitality; this.atk = memento.Atthack; this.def = memento.Defense; } }
同样的道理,它我们之前也认识过了,只是多了一点东西而已,但是这些并不会影响整个备忘录的框架,所以,我们从这里由此可以得出Originator类就是我们的实际业务类型,当然,我们大多数项目中一个对象(Model)几乎都是用来充当一个传值对象,不会像这样有业务逻辑在里面,在软件中这种现象它们都有专业的名词解释,我们称之为贫血模式(无业务逻辑)和充血模式(有业务逻辑),定义我最后再来说,我们先把备忘录模式,不,是游戏存档说完哈,好了备忘录的其中两个对象已经出来了,接下来第三位对象也要出来亮个相了,相信我们已经都猜到它了,没错,它就是用来保存游戏对象(备忘录对象)的对象
/// <summary> /// 角色状态管理者 /// </summary> class RoleStateCaretaker { private RoleStateMemento memento; public RoleStateMemento Memento { get { return memento; } set { memento = value; } } }
所以,上面这个对象就是个类型为RoleStateMemento对象的类型。到这里,我们的游戏存档就完成了,虽然不是真实的业务场景,但是已经比备忘录模式的框架要好理解的多了,接下来我们就来实现我们的游戏存档了
class Program { static void Main(string[] args) { //大战Boss前 GameRole lixiaoyao = new GameRole(); lixiaoyao.GetInitState(); lixiaoyao.StateDisplay(); //保存进度 RoleStateCaretaker stateAdmin = new RoleStateCaretaker(); stateAdmin.Memento = lixiaoyao.SaveState(); //大战Boss时,损耗严重 lixiaoyao.Fight(); lixiaoyao.StateDisplay(); //恢复之前状态 lixiaoyao.RecoveryState(stateAdmin.Memento); lixiaoyao.StateDisplay(); Console.Read(); } }
首先我们创建了一个李逍遥的角色(咳咳),这个名字随意,然后显示角色初始值,然后我们先保存我们创建的这个李逍遥对象,这样我们就可以义无反顾的去杀Boss了,由于Boss过于强大,我们损伤一半血,不行了,要撤了,不然要死了(哈哈,这里都是YY),然后我们就把我们之前的存档用来恢复,最后再显示人物血条,我们看运行结果吧。
好吧,这个也是似曾相识对吧,只是内容多了点而已,好了,我们的游戏存档也完成了,两种实现备忘录的方式完全都没有用到什么继承和接口。。。,下面,我们来说说官方名词。
备忘录模式:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态,它是一种对象行为模式,其别名为Token。
充血模式:领域对象中包含属性和业务逻辑的
贫血模式:领域对象中只包含属性,不包含业务逻辑的