前面說的設計模式幾乎都有用到繼承、介面,今天,我要說的這個設計模式,它就沒有用到任何介面,也不需要抽象類,更沒有複雜的繼承關係,它就是備忘錄模式,何為備忘錄,按照現實世界的理解,備忘錄就是人們在此刻記錄一些東西用作未來供我們回憶想起此刻所記錄的資訊,大白話就是記錄作用,我們今天的備忘錄也是一樣的道理。
備忘錄,首先當然是備忘錄類了,請看程式碼
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。
充血模式:領域物件中包含屬性和業務邏輯的
貧血模式:領域物件中只包含屬性,不包含業務邏輯的