備忘錄模式 - 設計模式學習

  備忘錄模式(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();
        }
    }  
}
複製代碼

  我的感受,雖然在客戶端隱藏了具體細節,可是好像那三個屬性有了重複代碼。

  要注意備忘錄模式的缺點,就是當角色狀態須要完整存儲到備忘錄對象中,若是狀態數據很大不少,那麼在資源消耗上,備忘錄對象會很是消耗內存。

 
 
分類: 設計模式
 
0
0
 
(請您對文章作出評價)
 
« 上一篇: 適配器模式 - 設計模式學習
» 下一篇: 組合模式 - 設計模式學習
相關文章
相關標籤/搜索