通俗易懂设计模式解析——备忘录模式

前言

  今天咱们来看看备忘录模式【MementoPattern】,咱们平时写文档的时候一不当心写错了一些字或者删除了一些东西怎么办呢?不用怕、Windows里面提供了Ctrl+Z,后退一步,能够一直后退。这个东西怎么实现的呢?咱们记得以前讲过一个命令模式。命令保存的是发起人的具体命令(对应的行为)、咱们今天讲的这个备忘录跟这个有点类似,可是备忘录模式保存的是发起人的状态(对应的数据结构、如属性)。咱们没作一步操做就保存一步操做以前的数据。当咱们Ctrl+Z后退时恢复前一步数据、彷佛就达到了咱们须要的目的。数据库

备忘录模式介绍

1、来由

  在软件系统中咱们常常会遇到一些状态的转变。在某些时刻咱们须要恢复、回溯以前的某个时间点的状态。若是咱们使用一个公共的接口来使其余对象获得获取这个对象、会暴露对象封装的细节。那么咱们如何在不破坏对象的封装性的同时恢复对象的某一时刻的状态呢?设计模式

2、意图

  在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象以外保存这个状态。浏览器

3、案例图

4、备忘录模式代码示例

咱们看上面的案例图,主要包含如下三个部分:bash

发起人:发起人角色负责对状态的记录,包含建立和恢复备忘数据。数据结构

备忘录:负责储存发起人对象的状态、在恢复备忘数据的时候提供发起人须要的状态。函数

管理员:负责保存备忘录对象、负责备忘录对象、使其不能被其余对象进行访问及操做。ui

接下来咱们看一个案例、关于手机照片备份的问题,有些时候由于操做失误引发的数据遗失问题。怎么去避免呢?对照片备份,而后在须要的时候进行备份数据恢复。咱们看下如歌经过代码来实现吧:this

namespace Memento_Pattern
{
    class MementoPattern
    {
    }
    #region 照片数据

    public class Photo 
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 地址
        /// </summary>
        public string Address { get; set; }
    }
    #endregion

    #region 发起人角色

    public sealed class Sponsor 
    {
        private List<Photo> _photo;
        public Sponsor(List<Photo> photos) 
        {
            if (photos == null)
                throw new Exception("请传入正确的数据源!");
            this._photo = photos;
        }
        public List<Photo> GetPhotos
        {
            get { return this._photo; }
            set { this._photo = value; }
        }

        /// <summary>
        /// 建立备忘录,保存状态数据
        /// </summary>
        /// <returns></returns>
        public Memento CreateMemento() 
        {
            return new Memento(new List<Photo>(this._photo));
        }

        /// <summary>
        /// 获取备忘录数据、恢复状态数据
        /// </summary>
        /// <param name="memento"></param>
        public void RestoreMemento(Memento memento) 
        {
            GetPhotos = memento._mementoList;
        }

        /// <summary>
        /// 展现数据
        /// </summary>
        public void ShowPhoto() 
        {
            Console.WriteLine($"目前用有照片{GetPhotos.Count}张:");
            foreach (var item in GetPhotos)
            {
                Console.WriteLine($"照片名称:{item.Name}。照片地址:{item.Address}");
            }
        }
    }
    #endregion

    #region 备忘录
    public sealed class Memento 
    {
        public List<Photo> _mementoList { get; private set; }

        /// <summary>
        /// 初始化存储数据
        /// </summary>
        /// <param name="MementoList"></param>
        public Memento(List<Photo> MementoList)
        {
            this._mementoList = MementoList;
        }

    }
    #endregion

    #region 管理员
    /// <summary>
    /// 一个备忘录数据处理
    /// </summary>
    public sealed class MementoManager 
    {
        public Memento  memento { get; set; }
    }复制代码
  #endregion复制代码
}复制代码

namespace Memento_Pattern
{
    class Program
    {

        static void Main(string[] args)
        {
            ///初始化数据
            List<Photo> photos = new List<Photo>();
            photos.Add(new Photo { Name = "第一张.jpg", Address = "https://img2018.cnblogs.com/blog/1470432/201910/11.jpg" });
            photos.Add(new Photo { Name = "第二张.jpg", Address = "https://img2018.cnblogs.com/blog/1470432/201910/22.jpg" });
            photos.Add(new Photo { Name = "第三张.jpg", Address = "https://img2018.cnblogs.com/blog/1470432/201910/33.jpg" });
            Sponsor sponsor = new Sponsor(photos);

            ///展现数据
            sponsor.ShowPhoto();

            ///保存状态数据到备忘录
            MementoManager mementoManager = new MementoManager();
            mementoManager.memento = sponsor.CreateMemento();

            ///删除一张照片
            Console.WriteLine();
            Console.WriteLine();
            photos.RemoveAt(0);
            sponsor.GetPhotos = photos;
            Console.WriteLine("删除后");
            sponsor.ShowPhoto();

            ///恢复备忘录数据
            ///
            Console.WriteLine();
            Console.WriteLine();
            sponsor.RestoreMemento(mementoManager.memento);
            Console.WriteLine("恢复后");
            sponsor.ShowPhoto();

        }
    }
}复制代码

  这里咱们能够看到 对照片的备份、而后删除以后完成恢复操做。这里针对的是一个备忘录的操做。spa

  咱们看下若是咱们使用备忘录进行屡次状态的保存而且选择性恢复数据是如何实现的吧。设计

  首先对管理员角色进行修改:

/// <summary>
    /// 多个备忘录数据处理
    /// </summary>
    public sealed class MementoManagers
    {
        public Dictionary<string, Memento> mementoList { get; set; }
        public MementoManagers() 
        {
            mementoList = new Dictionary<string, Memento>();
        }
    }复制代码

  而后咱们修改Main函数进行操做看下结果

static void Main(string[] args)
        {
            ///初始化数据
            List<Photo> photos = new List<Photo>();
            photos.Add(new Photo { Name = "第一张.jpg", Address = "https://img2018.cnblogs.com/blog/1470432/201910/11.jpg" });
            photos.Add(new Photo { Name = "第二张.jpg", Address = "https://img2018.cnblogs.com/blog/1470432/201910/22.jpg" });
            photos.Add(new Photo { Name = "第三张.jpg", Address = "https://img2018.cnblogs.com/blog/1470432/201910/33.jpg" });
            Sponsor sponsor = new Sponsor(photos);

            ///展现数据
            sponsor.ShowPhoto();

            ///保存状态数据到备忘录
            MementoManagers mementoManagers = new MementoManagers();
            mementoManagers.mementoList.Add("1", sponsor.CreateMemento()); 

            ///删除一张照片
            Console.WriteLine();
            Console.WriteLine();
            photos.RemoveAt(0);
            sponsor.GetPhotos = photos;
            Console.WriteLine("删除后");
            sponsor.ShowPhoto();
            mementoManagers.mementoList.Add("2", sponsor.CreateMemento());

            ///恢复备忘录数据
            ///
            while (true)
            {
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine($"目前有{mementoManagers.mementoList.Count}个备份数据,请输入序号选择备份数据恢复");
                var index = Console.ReadLine();
                sponsor.RestoreMemento(mementoManagers.mementoList.GetValueOrDefault(index));
                Console.WriteLine("恢复后");
                sponsor.ShowPhoto();
                Console.WriteLine("输入q退出");
                var q = Console.ReadLine();
                if (q=="q")
                {
                    break;
                }
            }
           

        }复制代码

使用场景及优缺点

1、使用场景

一、须要保存/恢复数据的场景可使用备忘录模式。

二、可提供回滚操做的场景可以使用备忘录模式、例如Ctrl+Z。

2、优势

一、给用户提供了一个恢复机制,能够回退到某个历史状态。

二、备忘录的状态由备忘录角色管理,备忘录由管理角色管理,备份数据和恢复数据由发起人管理。符合单一职责原则。

3、缺点

一、会消耗大量的内存,保存一次消耗一次。最终都会消耗较多内存。

总结

  到这里咱们就介绍完了备忘录模式。备忘录模式将对象的状态数据进行储存,保存在备忘录角色中。而后经过管理员角色进行管理。能够将对象回退到历史某一时刻的状态数据。在游戏中的存档可以使用此模式、Ctrl+Z回退可以使用此模式、还有浏览器回退历史、数据库事务管理。关于回退操做均可以使用此模式进行操做。这里咱们须要注意的是与命令模式进行区分。备忘录模式保存的是对象的状态数据。命令模式保存的是对象发起的命令也就是行为。备忘录模式是对行为状态的操做、命令模式是对行为序列的操做。

  用爱生活,你会使本身幸福!用爱工做,你会使不少人幸福! 

 欢迎你们扫描下方二维码,和我一块儿踏上设计模式的闯关之路吧!

相关文章
相关标签/搜索