20.Memento备忘录(行为型模式)

一:动机(Motivation)

<1>在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。

<2>如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性。

二:意图(Intent)

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
——《设计模式》GoF

三:结构(Structure)

在这里插入图片描述

四:结构详解

在这里插入图片描述

五:生活中的例子

<1>玩游戏(单机)时,需要经常保存进度,在必要的时候(如gameover),再读取存档,继续游戏(即S/L大法)。

<2>三个对象(与下图对应关系):
游戏 = 游戏角色
进度 = 角色状态存储箱
存取档工具 = 角色状态管理者

六:实现

namespace Test
{
    public class 游戏进度
    {
        public 游戏进度(int attack, int life, int defense)
        {
            this.attack = attack;
            this.life = life;
            this.defense = defense;
            this.createTime = DateTime.Now;
        }

        private int attack;
        public int Attack { get { return attack; } }

        private int life;
        public int Life { get { return life; } }

        private int defense;
        public int Defense { get { return defense; } }

        private DateTime createTime;
        public DateTime CreateTime { get { return createTime; } }

        public override string ToString()
        {
            return String.Format("{0},生命:{1},攻击:{2},防御:{3}",
                this.CreateTime, this.Life, this.Attack, this.Defense);
        }
    }

    public class 游戏
    {
        public 游戏()
        {
            this.attack = 100;
            this.life = 100;
            this.defense = 100;
            Console.WriteLine("游戏开始于:{0}", DateTime.Now);
        }

        private int attack;
        public int Attack
        {
            get { return this.attack; }
        }

        private int life;
        public int Life
        {
            get { return this.life; }
        }

        private int defense;
        public int Defense
        {
            get { return this.defense; }
        }
        public void 查看状态()
        {
            Console.WriteLine("当前生命力:{0},攻击力:{1},防御力:{2},游戏是否结束:{3}",
                this.Life, this.Attack, this.Defense, this.游戏结束);
        }

        public void 战斗()
        {
            Console.WriteLine("\t开始战斗!");
            System.Threading.Thread.Sleep(3000);
            int lifeless = new Random().Next(100);
            this.life = this.life - lifeless;
            int attackless = new Random().Next(100);
            this.attack = this.attack - attackless;
            int defenseless = new Random().Next(100);
            this.defense = this.defense - defenseless;
            Console.WriteLine("本回合战斗结束,损失:生命:{0},攻击:{1},防御:{2}",
                this.Life, this.Attack, this.Defense);
        }

        public bool 游戏结束
        {
            get { return this.Life <= 0; }
        }

        public 游戏进度 存档()
        {
            return new 游戏进度(this.attack, this.life, this.defense);
        }

        public void 读档(游戏进度 saved)
        {
            this.life = saved.Life;
            this.attack = saved.Attack;
            this.defense = saved.Defense;
            Console.WriteLine("读档结束:{0}", saved);
        }
    }

    /// <summary>
    /// careTaker
    /// </summary>
    public class 存取档工具
    {
        private System.Collections.Hashtable saves = new System.Collections.Hashtable();

        public void 存档(string 进度编号, 游戏进度 progress)
        {
            if (!this.saves.ContainsKey(进度编号))
            {
                this.saves.Add(进度编号, progress);
            }
            else
            {
                this.saves[进度编号] = progress;
            }
            Console.WriteLine("进度已保存,进度编号:{0},进度信息:{1}", 进度编号, progress);
        }

        public 游戏进度 读档(string 进度编号)
        {
            Console.WriteLine((this.saves[进度编号]).GetType());
            return this.saves[进度编号] as 游戏进度;
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            游戏 game = new 游戏();
            存取档工具 sltool = new 存取档工具();
            游戏进度 progress = game.存档();

            sltool.存档("000", progress);
            game.查看状态();  //查询初始状态

            Console.WriteLine("\n第一次战斗\n");
            game.战斗();  //第一次战斗
            game.查看状态();
            progress = game.存档();
            sltool.存档("001", progress);

            Console.WriteLine("\n第二次战斗\n");
            game.战斗();  //第二次战斗
            game.查看状态();
            progress = game.存档();
            sltool.存档("002", progress);

            Console.WriteLine("\n读档:000\n");
            progress = sltool.读档("000");
            game.读档(progress);
            game.查看状态();

            Console.WriteLine("\n读档:001\n");
            progress = sltool.读档("001");
            game.读档(progress);
            game.查看状态();

            Console.WriteLine("\n读档:002\n");
            progress = sltool.读档("002");
            game.读档(progress);
            game.查看状态();

            Console.ReadLine();
        }
    }
}

实现结果

在这里插入图片描述

七:实现要点

<1>让”游戏”自己来创建”进度”;
<2>“存取档工具”只是负责”进度”的读取,并不关心”进度”的内部细节;
<3>根据”进退”来重新初始化”游戏”内部细节的工作,应交给”游戏”自己实现;
<4>“存取档工具”可维护多个”进度”。

八:源码测试

using System.Text;

namespace Test
{
    public class Memento
    {
        public int Life { get; set; }

        public override string ToString()
        {
            return String.Format("当前生命:{0}", this.Life);
        }
    }

    public interface IOriginator
    {
        void Load(Memento memento);
        Memento Save();
    }

    public class Game : IOriginator
    {
        private int life = 100;

        public void Fight()
        {
            System.Threading.Thread.Sleep(2000);
            this.life -= new Random().Next(100);
        }

        public override string ToString()
        {
            return String.Format("当前生命值:{0},游戏结束?{1}", this.life, this.life < 1);
        }

        public void Load(Memento memento)
        {
            this.life = memento.Life;
        }

        public Memento Save()
        {
            return new Memento()
            {
                Life = this.life
            };
        }
    }

    public class CareTaker
    {
        private Dictionary<int, Memento> mementos = new Dictionary<int, Memento>();

        public void SaveMemento(int id, Memento memento)
        {
            this.mementos.Add(id, memento);
        }

        public Memento LoadMemento(int id)
        {
            return this.mementos[id];
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            foreach (var item in this.mementos)
            {
                sb.AppendFormat("存档ID:{0}---{1}\n", item.Key, item.Value);
            }
            return sb.ToString();
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            IOriginator originator = new Game();
            Console.WriteLine(originator);
            Memento m = originator.Save();
            Console.WriteLine(m);

            CareTaker careTaker = new CareTaker();
            careTaker.SaveMemento(1, m);  //第一次存档
            (originator as Game).Fight();
            Console.WriteLine(originator);

            m = originator.Save();
            Console.WriteLine(m);
            careTaker.SaveMemento(2, m);  //第二次存档
            (originator as Game).Fight();
            Console.WriteLine(originator);

            m = originator.Save();
            Console.WriteLine(m);
            careTaker.SaveMemento(3, m);  //第三次存档

            Console.WriteLine("------------------");
            Console.WriteLine(careTaker);

            ///还原
            Console.WriteLine("------------------");
            Game game = originator as Game;
            game.Load(careTaker.LoadMemento(3));
            Console.WriteLine(game);
            game.Load(careTaker.LoadMemento(2));
            Console.WriteLine(game);
            game.Load(careTaker.LoadMemento(1));
            Console.WriteLine(game);

            Console.ReadLine();
        }
    }
}

测试结果

在这里插入图片描述

九:适用性

<1>必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态;
<2>如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

十:总结

<1>备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态;
<2>Memento模式适用于“由原发器管理,却又必须存储在原发器之外的信息”;
<3>在实现Memento模式中,要防止原发器以外的对象访问备忘录对象。备忘录对象有两个接口,一个为原发器使用的宽接口;一个为其他对象使用的窄接口。
<4>在实现Memento模式时,要考虑拷贝对象状态的效率问题,如果对象开销比较大,可以采用某种增量式改变来改进Memento模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值