美文网首页.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