1. 程式人生 > >面向對象編程思想-備忘錄模式

面向對象編程思想-備忘錄模式

使用 存儲 exc 進度 通過 今天 time program tor

一、引言

上篇博文中我們分享了訪問者模式,訪問者模式是把作用於數據結構上的操作封裝到訪問者類中,使得數據結構與操作分離。今天我們要學習的備忘錄模式與命令模式有點相似,不同的是,命令模式保存的是發起人的具體命令(命令對應行為),而備忘錄模式保存的是發起人的狀態(狀態對應數據內部結構,如屬性)。下面請看今天要學習的訪問者模式

二、備忘錄模式

定義:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可將該對象恢復到原先保存的狀態

下面是備忘錄模式的結構圖:

技術分享

下面是備忘錄模式代碼demo:

技術分享
    //發起人
    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(); } }
View Code

技術分享

分析:把保存狀態的細節封裝到Memento中,這樣哪天更改保存的細節也不會影響客戶端。使用備忘錄模式可以將復雜對象的內部信息對其他對象屏蔽起來。當角色狀態改變時,有可能這個狀態無效,可以使用暫時存儲的備忘錄將狀態復原。

下面是多狀態多備份備忘錄實例:

技術分享
    //遊戲進度類
    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("索引超出界限");
                }
            }
        }
    }
View Code

技術分享

分析:實際應用中,大多情況下用到的是多狀態多備份,如果狀態很大很多,備忘錄對象會很耗內存

優點

1.如果某個操作錯誤破壞了數據的完整性,可以使用備忘錄模式恢復原來保存的數據

2.備份的狀態保存在發起人之外,這樣發起人就不需要對各個保存的狀態進行管理。

缺點

1.在實際系統中,可能需要多狀態多備份,對系統資源消耗是比較大的

適用場景

1.如果系統需要提供回滾操作時,使用備忘錄模式是比較方便的。例如數據庫中事務操作,文本編輯器中的Ctrl+Z撤銷操作

參考:

大話設計模式

http://www.cnblogs.com/zhili/p/MementoPattern.html;

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接。

面向對象編程思想-備忘錄模式