用心理解设计模式——备忘录模式 (Memento Pattern)

一、定义

  行为型模式之一。

  Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.

  (在不破坏封装性的前提下,捕获一个对象的内部状态并存储到外部,使得之后这个对象可以被恢复到此备份的状态。)

二、结构解析

  备忘录模式的一般结构有三种角色: 发起人、备忘录、备忘录管理员。

  发起者(Originator):有备忘需求的对象。可以创建备忘录,或通过备忘录恢复到备份的某一状态。

  备忘录(Memento):持有并只持有发起者某时刻的一个状态(相当于发起者的一次快照)。一般而言,备忘录被创建后,持有的状态应该是只读的,所以备忘录应该在构造时初始化,并且不提供改变状态的方法。备忘录只应被发起者使用

  备忘录管理者(CareTaker):可以当作是一个备忘录仓库,聚合/持有多个备忘录,可能采用字典、堆栈、列表等不同的集合存储备忘录,以应对不同的备忘需求。。发起者创建的备忘录将被放在此处,发起者恢复状态时也从这里获取备忘录。

三、评价

扫描二维码关注公众号,回复: 5938636 查看本文章

  备忘录模式又叫快照模式,用于备份系统某时刻的状态,常见于游戏存档、版本控制、浏览记录等。

  又可分为 “白箱备忘录” 和 “黑箱备忘录” 。区别是,黑箱备忘录持有的状态只对发起者公开,更安全(定义中的 “不破坏封装性”)。

  Java中,用 “备忘录作为发起者的内部类,并将备忘录的方法全部设为私有” 来实现黑箱备忘录。这种方式在C#中不可用!原因是,C#和Java的内部类性质有所区别:Java中,外部类内部可以直接访问内部类的私有成员,而在C#中不行。

  因此,我换了一种方式来实现了“黑箱”:备忘录中提供公有一个方法,反调“以参数形式传入的发起者”的改变状态接口。这样,虽然此方法是公开的,但只可能被发起者使用。

  或者也可以直接在备忘录构造时就引用创建它的发起者,之后只提供一个恢复接口,此时,备忘录就只能被创建它的发起者使用了。

  注意:在备份状态时,要小心处理引用类型(要对其进行深拷贝)。

四、实现

using System.Collections.Generic;
using UnityEngine;

namespace Memento
{
    //状态数据类用于辅助说明
    public struct State
    {
        public string name; //状态名
        //...状态的其他数据

        public State(string name/*, ...状态的其他数据*/)
        {
            this.name = name;
        }
    }
    //-------------------------------------

    //备忘录
    public class Memento
    {
        private State state;

        //一般而言,备忘录被创建后,持有的State应该是只读的,所以在构造时初始化,并且不提供SetState()方法
        public Memento(State state)
        {
            this.state = state;
        }

        //白箱备忘录
        //暴露了备忘录中的状态给任何对象
        //public State GetState()
        //{
        //    return state;
        //}

        //黑箱备忘录
        //提供一个方法,反调“以参数形式传入的Originator”的改变状态接口(或者将originator直接放在Memento构造时)。
        //这样, 虽然这个方法是公有的,但只可能被Originator使用, 保证了备忘录的安全性。缺点是, Memento反向依赖了Originator,增加了耦合度。
        public void Restore(Originator originator)
        {
            originator.ChangeState(this.state);
        }

        //另一种黑箱备忘录
        //Memento构造时就传入 originator对象
        //public void Restrore()
        //{
        //    originator.ChangeState(this.state);
        //}
    }

    public class Originator
    {
        private State state;

        //自身状态自然变化
        public void ChangeState(State state)
        {
            this.state = state;
        }

        //获取当前自身状态
        public State GetState()
        {
            return state;
        }

        //创建备忘录,保存当前状态
        public Memento CreateMemento()
        {
            return new Memento(state);
        }

        //使用备忘录恢复状态。
        //public void RestoreFromMemento(Memento Memento)
        //{
        //    this.state = Memento.GetState();
        //}

        public void RestoreFromMemento(Memento Memento)
        {
            Memento.Restore(this);
        }
    }

    public class CareTaker
    {
        private Dictionary<string, Memento> mementoDict = new Dictionary<string, Memento>();

        public void AddMemento(string stateName, Memento state)
        {
            mementoDict.Add(stateName, state);
        }

        public Memento GetMemento(string stateName )
        {
            return mementoDict[stateName];
        }
    }

    public class Client
    {
        public static void Main()
        {
            Originator originator = new Originator();
            CareTaker careTaker = new CareTaker();
            originator.ChangeState(new State("状态一"));
            originator.ChangeState(new State("状态二"));

            //存储当前状态
            string key = originator.GetState().name;
            careTaker.AddMemento(key, originator.CreateMemento());
            
            originator.ChangeState(new State("状态三"));

            //恢复到之前备份的某一状态
            originator.RestoreFromMemento(careTaker.GetMemento(key));

            Debug.Log("目前状态:" + originator.GetState().name);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/NRatel/article/details/84672854
今日推荐