通俗易懂設計模式解析——備忘錄模式

前言

  今天咱們來看看備忘錄模式【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回退可以使用此模式、還有瀏覽器回退歷史、數據庫事務管理。關於回退操做均可以使用此模式進行操做。這裏咱們須要注意的是與命令模式進行區分。備忘錄模式保存的是對象的狀態數據。命令模式保存的是對象發起的命令也就是行爲。備忘錄模式是對行爲狀態的操做、命令模式是對行爲序列的操做。

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

 歡迎你們掃描下方二維碼,和我一塊兒踏上設計模式的闖關之路吧!

相關文章
相關標籤/搜索