Unity3D 设计模式学习之备忘录模式

备忘录模式(Memento)在游戏中可用来做存储功能。在书中使用备忘录模式”存储成就记录”

直接将数据存盘功能实现于游戏功能类中,一般来说不太理想,因为违反了单一职责原则(SRP)。也就是说,各个游戏功能类应该专心处理与游戏相关的功能。

而记录保存,也应交由专职的记录保存类

为说明要专门写一个记录保存类呢?
书中给出了解释说明:
如果在功能模块中实现,会将内部成员数据全部公开。
虽然可以少写代码,缺违背了接口隔离原则(ISP)即除非必要,类应该尽量减少对外显示内部的数据接口,以减少客户端有机会破坏内部成员的记录,而对外公布过多的操作方法,容易增加与其他系统的耦合度。

GOF对备忘录模式的定义:
在不违反封装的原则下,获取一个对象的内部状态并保留在外部,让该对象可以在日后恢复到原先保留时的状态。

实现思路

这里列出几个问题,来做思考。
如何在“不违反封装的原则” 还能提供内部的详细信息?

就是由游戏系统本身“主动提供内部信息”存盘功能,而且也“主动”向存盘功能提供自己“系统”有关的信息。

这与原本由游戏本身提供一大堆“存取内部成员”的方法,有什么不同?最大的不同在于:
游戏系统提供存取内部成员方法,是让游戏系统处于“被动”状态。

备忘录模式则是将游戏系统由“被动”改为“主动提供”,意思是,
游戏系统自己决定要提供什么信息和记录存盘功能,也由游戏系统决定要从存盘功能中,读取什么样的数据及记录还原给内部成员。

而这些信息记录的设置和获取的实现地点都在“游戏系统类内”,不会发生在游戏系统类以外的地方,如此就可确保类的“封装的原则”不会被破坏。

备忘录模式的说明

记录拥有者Originator
记录保存者Memento
管理记录保存者Caretaker

记录拥有者Originator
public class Originator{
    string m_State; //状态,需要被保存

    public void SetInfo(string State){
        m_State = State;
    }
    public void ShowInfo(){
        Debug.Log("Originator State"+m_State);
    }
    //产生要存储的记录
    public Memento CreateMemento(){
        Memeto newMemento = new Memento();
        newMemento.SetState(m_State);
        return newMemento;
    }
    //设置要恢复的记录
    public void SetMemento(Memento m){
        m_State = m.GetState();
    }
}

Originator(记录拥有者)类内拥有一个需要被保存的成员:m_State,而这个成员将由Originator(记录拥有者)在CreateMemento方法中产生记录保存者对象,最后传出到客户端。

客户端可以将之前保留的记录保存者对象通过记录拥有者的类方法:SetMemento传入类中,让Originator可以恢复到之前记录的状态。

Memento 存放在记录拥有者(Originator)的内部状态
public class Memento{
    string m_State;
    public string GetState(){
        return m_State;
    }
    public void SetState(string State){
        m_State = State;
    }
}

很简单的一个类,封装了数据,通过Set Get 方法来读取、存储数据

测试备忘录模式
void UnitTest(){
    Originator theOriginator = new Originator();
    //设置信息
    theOriginator.SetInfo("Step1");
    theOriginator.ShowInfo();
    //存储状态
    Memento theMemento = theOriginator.CreateMemento();
    //设置新的信息
    theOriginator.SetInfo("Step2");
    theOriginator.ShowInfo();
    //恢复
    theOriginator.SetMemento(theMemento);
    theOriginator.ShowInfo();
}

//测试代码

在测试代码中,先将记录拥有者设置为Step1之后,利用CreateMemento方法将内部状态保留下来(theMemento)。
随后设置为“Step2”,但假设此时设置发生错误,只要将之前保留的状态,利用SetMemento方法再设置回去就可以了

Caretaker保管所有的Memento(记录),并且管理它们
public class Caretaker{
    Dictionary<string,Memento> m_Mementos = new Dictionary<string,Memento>();
    //增加
    public void AddMemento(string Version,Memento theMemento){
        if(m_Mementos.ContainsKey(Version) == false) 
            m_Menmentos.Add(Version,theMemento);
        else{
            m_Mementos[Version] = theMemento;
        }
    }
    //读取
    public Memento GetMemento(string Version){
        if(m_Nementos.CointainsKey(Version)) == false){
            return null;
        }
        return m_Mementos[Version];
    }
}
测试管理记录保存者
void UnitTest(){
    Originator theOriginator = new Originator();
    CareteKer theCaretaker = new Caretaker();

    //设置信息
    theOriginator.SetInfo("Version1");
    theOriginator.ShowInfo();
    //保存
    theCaretaker.AddMemento("1",theOriginator.CreateMemento());

    //设置信息
    theOriginator.SetInfo("Version2");
    theOriginator.ShowInfo();
    //保存
    theCaretaker.AddMemento("2",theOriginator.CreateMemento());

    //设置信息
    theOriginator.SetInfo("Version3");
    theOriginator.ShowInfo();
    //保存
    theCaretaker.AddMemento("3",theOriginator.CreateMemento());

    ///退回到第2版
    theOriginator.SetMementor(theCaretaker.GetMemento("2"));
    theOriginator.ShowInfo();
    //退回到第1版
    theOriginator.SetMemento(theCaretaker.GetMemento("1"));
    theOriginator.ShowInfo();
}

第二个测试加入了记录对象管理类(Caretaker),记录拥有者(theOriginator)就转而向记录管理类找到自己需要的版本,通过过字典容器找到获取这个记录(Memento)

总结

运用备忘录模式Memento 提供了一个不破坏原有类封装性的“对象状态保存”方案,并让对象状态保存可以存在多个版本,并且还可以选择要恢复到的哪个版本。

与其他模式的合作:
如果备忘录模式搭配命令模式来作为命令执行前的系统状态保存,就能让命令在执行恢复操作时,能够恢复到命令执行前的状态。

其他应用方式
日志系统用备忘录模式,让游戏系统产生的信息定期存储。此时日志系统就只负责存储记录。

猜你喜欢

转载自blog.csdn.net/liaoshengg/article/details/82530426
今日推荐