今天要介紹的模式是 Memento ,簡單來說就是備忘錄的概念,也可以想像成用來記錄之前狀態的一個模式,透過 Memento 模式可以將狀態做儲存,如果未來有需要將狀態回復到跟之前一樣,就可以馬上地做狀態回復,我們就直接來看例子吧!
保存一個對象的某個狀態,以便在適當的時候恢復對象。
(圖片來源:http://www.w3big.com/design-pattern/memento_pattern_uml_diagram.jpg)
Memento
是作為紀錄狀態所使用的物件,在做紀錄數值時,並不是直接將數值存進CareTaker
,而是先將數值轉換成Memento
裡的屬性,再將Memento
存入CareTaker
裡用來記錄的List
,好處是可以將Player
以及CareTaker
解耦合,雙方不會直接地引用,而是透過Memento
來做介接。using System;
using System.Collections.Generic;
namespace DAY25_Memento
{
public class Program
{
static void Main(string[] args)
{
Player Player = new Player();
CareTaker careTaker = new CareTaker();
Monster monster = new Monster();
careTaker.Add(Player.SaveHpToMemento()); // 儲存生命值
monster.Attack(Player);
careTaker.Add(Player.SaveHpToMemento()); // 儲存生命值
monster.Attack(Player);
monster.Attack(Player);
careTaker.Add(Player.SaveHpToMemento()); // 儲存生命值
monster.Attack(Player);
Player.GetHpFromMemento(careTaker.Get(0)); // 回復至先前生命值
Player.GetHpFromMemento(careTaker.Get(1)); // 回復至先前生命值
Player.GetHpFromMemento(careTaker.Get(2)); // 回復至先前生命值
}
}
public class Memento
{
private int _state;
public Memento(int state)
{
_state = state;
}
public int GetState()
{
return _state;
}
}
public class Player
{
private int _hp;
public Player()
{
_hp = 100;
}
public int GetHp()
{
return _hp;
}
public void SetHp(int hp)
{
_hp = hp;
}
// 將現有生命值儲存至Memento
public Memento SaveHpToMemento()
{
Console.WriteLine($"玩家儲存目前生命值{_hp}點,成功!");
return new Memento(_hp);
}
// 設定玩家回復至先前生命值
public void GetHpFromMemento(Memento memento)
{
_hp = memento.GetState();
Console.WriteLine($"玩家回復至上次儲存的生命值{_hp}點,成功!");
}
}
public class Monster
{
public void Attack(Player player)
{
Random rnd = new Random();
var player_hp = player.GetHp();
var attack_hp = rnd.Next(player_hp);
var remain_hp = player_hp - attack_hp;
player.SetHp(remain_hp);
Console.WriteLine($"猛獸攻擊{attack_hp}點,玩家原本{player_hp}點,目前剩{remain_hp}點\n");
}
}
public class CareTaker
{
private List<Memento> _mementoList = new List<Memento>();
// 存入Memento
public void Add(Memento state)
{
_mementoList.Add(state);
}
// 取得指定Memento
public Memento Get(int index)
{
return _mementoList[(_mementoList.Count-1) - index];
}
}
}
利用Memento
,讓Player
以及CareTaker
不會互相地引用,反而是更有彈性能去做靈活的變化,當今天如果是Player
以外的類別也想要做紀錄,假設Monster
也要記錄生命值,一樣可以使用Memento
做紀錄,或者是將Memento
做成抽象類別,這樣當Monster
要記錄的不只是生命值,可能還有其他數值時,就能夠用一個MonsterMemento
去繼承Memento
,達成抽共用以及多型的實現。