摘要:备忘录模式我们常见的应用有编辑器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();
}
}
}
网友评论