美文网首页.NETdotNETASP.NET Core见识录
备忘录模式-优雅地备份还原

备忘录模式-优雅地备份还原

作者: Carson_jz | 来源:发表于2019-11-04 15:46 被阅读0次

    摘要:备忘录模式我们常见的应用有编辑器ctrl+z可撤销操作,浏览器返回上一次浏览网址,文章撤销编辑等,他们有个共通点,在于备份还原,专业的术语描述备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态或者数据,备份起来,可以随时还原。设计模式Github源码

    要实现备忘录模式,总结有三个类:

    Model(源数据):是一个普通类/数据实体,你需要对这个类的数据/状态进行备份,为了不破坏这个类的封装性,备份的操作以及存储外置

    BackUpModel(备份类):用于备份Model,相当于Model的镜像类

    Caretaker(负责人):它是用于执行备份与还原的,它存储着Model的备份版本BackUpModel的数据

    引用关系:

    Model : BackUpModel

    Caretaker : BackUpModel

    至于为什么要弄一个BackUpModel作为备份类, 用Caretaker去管理备份类这么麻烦呢?直接在Model里加一个自己对象作为属性来备份不可吗(刚开始编程时候就喜欢啥都堆一起贼方便)?

    这个就涉及到程序的设计理念,我们想象一个需求,做一个文章编辑的,一开始我们的需求是可以在草稿箱编辑文章,点发布才正式发布,那文章是实体,我们把文章的草稿当成文章的属性,发布时候只需要重备份实体属性取出来覆盖便可,这样做一开始是没有问题的,但到后来需求变更了,需要把备份变为多个,可以记录多个操作的记录,这时候我们是不是要把原来的草稿属性变为List,这样做就违反了:

    开闭原则

    开闭原则的英文全称是Open Close Principle缩写即OCP。开闭原则的定义是:软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是对于修改是封闭的。在软件的生命周期内,因为变化、升级和维护等原因需要对软件的原有代码进行修改时,可能会将错误的代码引入,从而破坏原有系统。因此当软件需求发生变化时,我们应该尽量通过扩展的方式 来实现变化,而不是通过修改已有的代码。

    如何不破坏封装性而优雅的实现备份还原功能呢?

    便是通过结构备份还原操作,新建个BackUpModel实体,原有的Model只需要一个RestoreMemento方法,属性就是自己的备份实体类,以后一切关于备份还原功能变更都不需要对Model修改了,只要修改Caretaker类便可。

    程序实现

    c#版本的文章编辑回滚操作

    using System;
    
    namespace MementoPattern
    {
        /// <summary>
        /// 文章备份实体(某个时刻文章数据状态)
        /// </summary>
        public class ArticleBackup
        {
            public string Title { get; set; }
            public string Content { get; set; }
    
            public ArticleBackup(string title, string content)
            {
                Title = title;
                Content = content;
            }
        }
    
        /// <summary>
        /// 文章操作管理者
        /// </summary>
        public class ArticleCaretaker
        {
            public ArticleBackup ArticleBackup { get; set; }
        }
    
        /// <summary>
        /// 文章实体
        /// </summary>
        public class Article
        {
            public string Title { get; set; }
            public string Content { get; set; }
    
            public Article(string title, string content)
            {
                Title = title;
                Content = content;
            }
    
            public bool TryUpdate(string title, string content)
            {
                //code...
                Title = title;
                Content = content;
                return true;
            }
    
            public ArticleBackup Backup()
            {
                return new ArticleBackup(Title, Content);
            }
    
            public void RestoreMemento(ArticleBackup model)
            {
                Title = model.Title;
                Content = model.Content;
            }
        }
    
        public class Program
        {
            static void Main(string[] args)
            {
                var article = new Article("title", "content");
    
                //备份
                var messageModelCaretaker = new ArticleCaretaker
                {
                    ArticleBackup = article.Backup()
                };
    
                //更新数据
                article.TryUpdate("new title", "new content");
    
                Console.WriteLine($"{article.Title},{article.Content}");
    
                //回滚数据
                article.RestoreMemento(messageModelCaretaker.ArticleBackup);
    
                Console.WriteLine($"{article.Title},{article.Content}");
    
                Console.ReadKey();
            }
        }
    }
    
    

    c#版本的文章的版本功能

    using System;
    using System.Collections.Generic;
    
    namespace MementoPatternExt
    {
        /// <summary>
        /// 文章备份实体(某个时刻文章数据状态)
        /// </summary>
        public class ArticleBackup
        {
            public string Title { get; set; }
            public string Content { get; set; }
    
            public ArticleBackup(string title, string content)
            {
                Title = title;
                Content = content;
            }
        }
    
        /// <summary>
        /// 文章操作管理者
        /// </summary>
        public class ArticleCaretaker
        {
            /// <summary>
            /// 当前版本号
            /// </summary>
            public int CurrentBatch { get; set; }
            public List<ArticleBackup> ArticleBackupList { get; set; } = new List<ArticleBackup>();
    
            /// <summary>
            /// 获取上一个版本
            /// </summary>
            /// <returns></returns>
            public ArticleBackup GetPre()
            {
                if (CurrentBatch > 1)
                {
                    return ArticleBackupList[CurrentBatch - 2];
                }
                else
                {
                    throw new ArgumentException();
                }
            }
    
            /// <summary>
            /// 获取指定版本
            /// </summary>
            /// <param name="batch"></param>
            /// <returns></returns>
            public ArticleBackup GetByBatch(int batch)
            {
                return ArticleBackupList[batch - 1];
            }
    
            /// <summary>
            /// 备份新版本
            /// </summary>
            /// <param name="articleBackup"></param>
            public void Set(ArticleBackup articleBackup)
            {
                ArticleBackupList.Add(articleBackup);
                CurrentBatch++;
            }
        }
    
        /// <summary>
        /// 文章实体
        /// </summary>
        public class Article
        {
            public string Title { get; set; }
            public string Content { get; set; }
    
            public Article(string title, string content)
            {
                Title = title;
                Content = content;
            }
    
            public bool TryUpdate(string title, string content)
            {
                //code...
                Title = title;
                Content = content;
                return true;
            }
    
            public ArticleBackup Backup()
            {
                return new ArticleBackup(Title, Content);
            }
    
            public void RestoreMemento(ArticleBackup model)
            {
                Title = model.Title;
                Content = model.Content;
            }
        }
    
        public class Program
        {
            static void Main(string[] args)
            {
                //版本1
                var article = new Article("title1", "content1");
    
                var messageModelCaretaker = new ArticleCaretaker();
                messageModelCaretaker.Set(article.Backup());
    
                article.TryUpdate("title2", "new content2");
                messageModelCaretaker.Set(article.Backup());
                article.TryUpdate("title3", "new content3");
                messageModelCaretaker.Set(article.Backup());
                article.TryUpdate("title4", "new content4");
                messageModelCaretaker.Set(article.Backup());
    
                Console.WriteLine($"{article.Title},{article.Content}");
    
                //回滚上一个版本数据
                article.RestoreMemento(messageModelCaretaker.GetPre());
                Console.WriteLine($"获取上一个版本数据{article.Title},{article.Content}");
    
                article.RestoreMemento(messageModelCaretaker.GetByBatch(1));
                Console.WriteLine($"获取首个版本数据{article.Title},{article.Content}");
    
                Console.ReadKey();
            }
        }
    }
    
    

    相关文章

      网友评论

        本文标题:备忘录模式-优雅地备份还原

        本文链接:https://www.haomeiwen.com/subject/bjsdbctx.html