iT邦幫忙

2022 iThome 鐵人賽

DAY 25
1

今天要介紹的模式是 Memento ,簡單來說就是備忘錄的概念,也可以想像成用來記錄之前狀態的一個模式,透過 Memento 模式可以將狀態做儲存,如果未來有需要將狀態回復到跟之前一樣,就可以馬上地做狀態回復,我們就直接來看例子吧!

Memento- 定義

保存一個對象的某個狀態,以便在適當的時候恢復對象。

https://ithelp.ithome.com.tw/upload/images/20221002/20136443CyZ5bGNrSd.png

(圖片來源:http://www.w3big.com/design-pattern/memento_pattern_uml_diagram.jpg)

範例 UML

https://ithelp.ithome.com.tw/upload/images/20221002/201364432VzadF0hUD.png

Code要點

  • Memento是作為紀錄狀態所使用的物件,在做紀錄數值時,並不是直接將數值存進CareTaker,而是先將數值轉換成Memento裡的屬性,再將Memento存入CareTaker裡用來記錄的List,好處是可以將Player以及CareTaker解耦合,雙方不會直接地引用,而是透過Memento來做介接。

不囉嗦上Code!

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];
        }
    }
}
  • 結果

https://ithelp.ithome.com.tw/upload/images/20221002/20136443rIUFmKccgM.png

簡單的小結

利用Memento,讓Player以及CareTaker不會互相地引用,反而是更有彈性能去做靈活的變化,當今天如果是Player以外的類別也想要做紀錄,假設Monster 也要記錄生命值,一樣可以使用Memento做紀錄,或者是將Memento做成抽象類別,這樣當Monster要記錄的不只是生命值,可能還有其他數值時,就能夠用一個MonsterMemento去繼承Memento,達成抽共用以及多型的實現。


上一篇
【DAY24】Proxy模式 - 找個代理人來幫忙你吧!
下一篇
【DAY26】Mediator模式 - 瞭解通訊軟體如何去發送訊息
系列文
勇闖秘境!探索物件導向背後的設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言