返回

15.备忘录模式

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

备忘录模式

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

备忘录模式的定义

如果以“游戏存盘”的功能来解释备忘录模式的定义,会更简单些,也就是在不增加各个游戏系统成员的“存取”方法的前提之下,存盘功能要能够获取各个游戏系统内部需要保存的信息,然后在游戏重新开始时,再将记录读取,并重新设置给各个游戏的系统。

备忘录模式的概念,是让有记录保存需求的类,自行产生要保存的数据,外界完全不用了解这些记录产生的过程及来源。另外,也让类自己从之前保存数据中找回信息,自行重设类的状态。

参与者说明如下:

  • Originator(记录拥有者)
    • 拥有记录的类,内部有成员或记录需要被存储。
    • 会自动将要保存的记录产生出来,不必提供存储内部状态的方法。
    • 会自动将数据从之前的保存记录中取回,并恢复到之前的状态。
  • Memento(记录保存者)
    • 负责保存 Originator(记录拥有者)的内部信息
    • 无法获取 Originator(记录拥有者)的信息,必须由 Originator(记录拥有者)主动设置和读取
  • Caretaker(管理记录保存者)
    • 管理 Originator(记录拥有者)产生出来的 Memento(记录保存者)
    • 可以增加对象管理容器来保存多个 Memento(记录保存者)

具体实现

    // 存放Originator物件的內部狀態
    public class Memento
    {
        string m_State;

        public string GetState()
        {
            return m_State;
        }

        public void SetState(string State)
        {
            m_State = State;
        }
    }

    // 需要儲存內容資訊
    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()
        {
            Memento newMemento = new Memento();
            newMemento.SetState(m_State);
            return newMemento;
        }

        // 設定要回復的記錄
        public void SetMemento(Memento m)
        {
            m_State = m.GetState();
        }
    }

    // 保管所有的Memento
    public class Caretaker
    {
        Dictionary<string, Memento> m_Memntos = new Dictionary<string, Memento>();

        // 增加
        public void AddMemento(string Version, Memento theMemento)
        {
            if (m_Memntos.ContainsKey(Version) == false)
                m_Memntos.Add(Version, theMemento);
            else
                m_Memntos[Version] = theMemento;
        }

        // 取回
        public Memento GetMemento(string Version)
        {
            if (m_Memntos.ContainsKey(Version) == false)
                return null;
            return m_Memntos[Version];
        }
    }

成就记录保存的功能设计

参与者说明:

  • AchievementSystem:成就系统拥有多项成就记录需要被存储,所以提供CreateSaveData方法让外界获取“存盘记录”,并利用SetSaveData方法将“之前存储的成就记录”回存。
  • AchievementSaveData:记录成就系统中需要被存盘的成就记录,并实现Unity3D中的数据保存功能。
  • PBaseDefenseGame:配合游戏启动和关闭,适时地调用成就系统的CreateSaveData、SetSaveData方法,来实现成就记录的保存和读取。

具体实现

// 成就記錄存檔
public class AchievementSaveData
{
    // 成就要存檔的資訊
    public int EnemyKilledCount { get; set; }
    public int SoldierKilledCount { get; set; }
    public int StageLv { get; set; }

    public AchievementSaveData()
    {
    }

    public void SaveData()
    {
        PlayerPrefs.SetInt("EnemyKilledCount", EnemyKilledCount);
        PlayerPrefs.SetInt("SoldierKilledCount", SoldierKilledCount);
        PlayerPrefs.SetInt("StageLv", StageLv);
    }

    public void LoadData()
    {
        EnemyKilledCount = PlayerPrefs.GetInt("EnemyKilledCount", 0);
        SoldierKilledCount = PlayerPrefs.GetInt("SoldierKilledCount", 0);
        StageLv = PlayerPrefs.GetInt("StageLv", 0);
    }
}


// 成就系統
public class AchievementSystem : IGameSystem
{
    private AchievementSaveData m_LastSaveData = null; // 最後一次的存檔資訊

    // 記錄的成就項目
    private int m_EnemyKilledCount = 0;
    private int m_SoldierKilledCount = 0;
    private int m_StageLv = 0;

    public AchievementSystem(PBaseDefenseGame PBDGame) : base(PBDGame)
    {
        Initialize();
    }

    // 
    public override void Initialize()
    {
        base.Initialize();

        // 註冊相關觀測者
        m_PBDGame.RegisterGameEvent(ENUM_GameEvent.EnemyKilled, new EnemyKilledObserverAchievement(this));
        m_PBDGame.RegisterGameEvent(ENUM_GameEvent.SoldierKilled, new SoldierKilledObserverAchievement(this));
        m_PBDGame.RegisterGameEvent(ENUM_GameEvent.NewStage, new NewStageObserverAchievement(this));
    }

    // 增加Enemy陣亡數
    public void AddEnemyKilledCount()
    {
        //Debug.Log ("AddEnemyKilledCount");
        m_EnemyKilledCount++;
    }

    // 增加Soldier陣亡數
    public void AddSoldierKilledCount()
    {
        //Debug.Log ("AddSoldierKilledCount");
        m_SoldierKilledCount++;
    }

    // 目前關卡
    public void SetNowStageLevel(int NowStageLevel)
    {
        //Debug.Log ("SetNowStageLevel");
        m_StageLv = NowStageLevel;
    }

    // 產生存檔
    public AchievementSaveData CreateSaveData()
    {
        AchievementSaveData SaveData = new AchievementSaveData();

        // 設定新的高分者
        SaveData.EnemyKilledCount = Mathf.Max(m_EnemyKilledCount, m_LastSaveData.EnemyKilledCount);
        SaveData.SoldierKilledCount = Mathf.Max(m_SoldierKilledCount, m_LastSaveData.SoldierKilledCount);
        SaveData.StageLv = Mathf.Max(m_StageLv, m_LastSaveData.StageLv);

        return SaveData;
    }

    // 設定舊的存檔
    public void SetSaveData(AchievementSaveData SaveData)
    {
        m_LastSaveData = SaveData;
    }

    // 儲存記錄
    /*public void SaveData()
    {
        PlayerPrefs.SetInt("EnemyKilledCount"	,m_EnemyKilledCount);
        PlayerPrefs.SetInt("SoldierKilledCount"	,m_SoldierKilledCount);
        PlayerPrefs.SetInt("StageLv"		 	,m_StageLv);
    }

    // 取回記錄
    public void LoadData()
    {
        m_EnemyKilledCount 	= PlayerPrefs.GetInt("EnemyKilledCount",0);
        m_SoldierKilledCount= PlayerPrefs.GetInt("SoldierKilledCount",0);
        m_StageLv 			= PlayerPrefs.GetInt("StageLv",0);
    }*/
}

public class PBaseDefenseGame
{
	....
	// 存檔
	private void SaveData()
	{
		AchievementSaveData SaveData = m_AchievementSystem.CreateSaveData();
		SaveData.SaveData();
	}

	// 取回存檔
	private AchievementSaveData LoadData()
	{
		AchievementSaveData OldData = new AchievementSaveData();
		OldData.LoadData();
		m_AchievementSystem.SetSaveData( OldData );
		return OldData;
	}
}

使用备忘录模式的优点

再运用备忘录模式之后,记录成就的功能就从成就系统中独立出来,让专职的AchievementSaveData类负责存盘的工作,至于AchievementSaveData类该怎么实现存盘功能,成就系统也不必知道。并且成就系统本身也保留封装性,不必对外开放过多的存取函数来获取内部的状态,信息的设置和恢复也都在成就系统内部完成。

备忘录模式的应用

游戏服务器常常需要针对执行性能进行分析及追踪,所以要定期地让各个系统产生日志,汇报各个游戏系统当前的执行情况,如内存的使用、执行时占用的事件、数据存取的频率等。要让系统日志功能更有弹性的话,可以使用备忘录模式让各个游戏系统产生要定期汇报的信息,并将信息内容的产生交给各个游戏系统,而日志系统只负责存储记录。