面向对象编程思想-备忘录模式

一、引言

上篇博文中我们分享了访问者模式,访问者模式是把作用于数据结构上的操作封装到访问者类中,使得数据结构与操作分离。今天我们要学习的备忘录模式与命令模式有点相似,不同的是,命令模式保存的是发起人的具体命令(命令对应行为),而备忘录模式保存的是发起人的状态(状态对应数据内部结构,如属性)。下面请看今天要学习的访问者模式

二、备忘录模式

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态

下面是备忘录模式的结构图:

下面是备忘录模式代码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;

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

posted @ 2017-08-26 17:34  快跑啊兔兔  阅读(244)  评论(0编辑  收藏  举报