備忘錄模式(Memento):在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。這樣之後就可將該對象恢復到原先保存的狀態。javascript
如下給出備忘錄模式的UML圖:html
Originator(發起人):負責建立一個備忘錄Memento,用以記錄當前時刻它的內部狀態,並可以使用備忘錄恢復內部狀態。Originator可根據須要決定Memento存儲Originator的哪些內部狀態。java
Memento(備忘錄):負責存儲Originnator對象的內部狀態,並可防止Originator之外的其餘對象訪問備忘錄Memento,備忘錄有兩個接口,Caretaker只能看到備忘錄的窄接口,它只能將備忘錄傳遞給其餘對象。git
Caretaker(管理者):負責保存好備忘錄Memento,不能對備忘錄的內容進行操做或檢查。設計模式
如下給出備忘錄模式的基本代碼結構:post
namespace ConsoleApplication1 { class Originator { private string state; public string State //須要保存的屬性,可能有多個 { get { return state; } set { state = value; } } public Memento CreateMemento() //建立備忘錄,將當前須要保存的信息導入並實例化出一個Memento對象 { return (new Memento(state)); } public void SetMemento(Memento memento) { state = memento.State; //恢復備忘錄,將Memento導入並將相關數據恢復 } 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; } } } //管理者(Caretaker)類 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(); //Originator初始狀態,狀態屬性爲"On" o.State = "On"; o.Show(); Caretaker c = new Caretaker(); //保存狀態時,因爲有了很好的封裝,能夠隱藏Originator c.Memento = o.CreateMemento(); o.State = "Off"; //Originator改變了狀態屬性爲"Off" o.Show(); o.SetMemento(c.Memento); //恢復原初始狀態 o.Show(); Console.ReadKey(); } } }
Memento模式比較適用於功能比較複雜的,但須要維護或記錄屬性歷史的類,或者須要保存的屬性只是衆多屬性中的一小部分時,Originator能夠根據保存的Memento信息還原到前一狀態。若是在某個系統中使用命令模式時,須要實現命令的撤銷功能,那麼命令模式可使用備忘錄模式來存儲可撤銷操做的狀態。學習
當角色的狀態改變的時候,有可能這個狀態無效,這時候就可使用暫時存儲起來的備忘錄將狀態復原。this
回到《大話設計模式》中保存遊戲進度的例子:spa
這是未使用備忘錄模式的代碼,註釋中註明其缺點。設計
namespace ConsoleApplication1 { class GameRole { //生命力 private int vit; public int Vitality { get { return vit; } set { vit = value; } } //攻擊力 private int atk; public int Attack { get { return atk; } set { atk = value; } } //防護力 private int def; public int Defense { get { return def; } set { def = value; } } //狀態顯示 public void StateDisplay() { Console.WriteLine("角色當前狀態:"); Console.WriteLine("體力:{0}",this.vit); Console.WriteLine("攻擊力{0}",this.atk); Console.WriteLine("防護力{0}",this.def); Console.WriteLine(""); } //得到初始狀態 public void GetInitState() { this.vit = 100; this.atk = 100; this.def = 100; } //戰鬥 public void Fight() { this.vit = 0; this.atk = 0; this.def = 0; } } class Program { static void Main(string[] args) { //大戰Boss前 GameRole lixiaoyao = new GameRole(); lixiaoyao.GetInitState(); //大戰Boss前,得到初始角色狀態 lixiaoyao.StateDisplay(); //保存進度 GameRole backup = new GameRole(); backup.Vitality = lixiaoyao.Vitality; //缺點:向客戶端暴露了實現細節 backup.Attack = lixiaoyao.Attack; backup.Defense = lixiaoyao.Defense; //大戰Boss時,拉柴了 lixiaoyao.Fight(); lixiaoyao.StateDisplay(); //恢復以前狀態 lixiaoyao.Vitality = backup.Vitality; lixiaoyao.Attack = backup.Attack; lixiaoyao.Defense = backup.Defense; lixiaoyao.StateDisplay(); Console.ReadKey(); } } }
下面來看看使用了備忘錄模式的代碼:
namespace ConsoleApplication1 { class GameRole { //生命力 private int vit; public int Vitality { get { return vit; } set { vit = value; } } //攻擊力 private int atk; public int Attack { get { return atk; } set { atk = value; } } //防護力 private int def; public int Defense { get { return def; } set { def = value; } } //狀態顯示 public void StateDisplay() { Console.WriteLine("角色當前狀態:"); Console.WriteLine("體力:{0}",this.vit); Console.WriteLine("攻擊力{0}",this.atk); Console.WriteLine("防護力{0}",this.def); Console.WriteLine(""); } //得到初始狀態 public void GetInitState() { this.vit = 100; this.atk = 100; this.def = 100; } //戰鬥 public void Fight() { this.vit = 0; this.atk = 0; this.def = 0; } //保存角色狀態 public RoleStateMemento SaveState() { return (new RoleStateMemento(vit, atk, def)); } //恢復角色狀態 public void RecoveryState(RoleStateMemento memento) { this.vit = memento.Vitality; this.atk = memento.Attack; this.def = memento.Defense; } } 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; } //生命力 public int Vitality { get { return vit; } set { vit = value; } } //攻擊力 public int Attack { get { return atk; } set { atk = value; } } public int Defense { get { return def; } set { def = value; } } } //角色狀態管理者類 class RoleStateCaretaker { private RoleStateMemento memento; public RoleStateMemento Memento { get { return memento; } set { memento = value; } } } class Program { static void Main(string[] args) { //大戰Boss前 GameRole lixiaoyao = new GameRole(); lixiaoyao.GetInitState(); //遊戲角色初始狀態,三種指標數據均是100 lixiaoyao.StateDisplay(); //保存進度 RoleStateCaretaker stateAdmin = new RoleStateCaretaker(); //保存遊戲進度,因爲封裝在Memento中,所以咱們並不知道保存了哪些具體的角色數據 stateAdmin.Memento = lixiaoyao.SaveState(); //大戰Boss時,拉柴了 lixiaoyao.Fight(); lixiaoyao.StateDisplay(); //恢復以前狀態 lixiaoyao.RecoveryState(stateAdmin.Memento); lixiaoyao.StateDisplay(); lixiaoyao.StateDisplay(); Console.ReadKey(); } } }
我的感受,雖然在客戶端隱藏了具體細節,可是好像那三個屬性有了重複代碼。
要注意備忘錄模式的缺點,就是當角色狀態須要完整存儲到備忘錄對象中,若是狀態數據很大不少,那麼在資源消耗上,備忘錄對象會很是消耗內存。