--- tags: Behavioral Patterns --- # 備忘錄模式 Memento ## 角色 **Originator** 就是定義中提到的要保留內部狀態的物件。現實例子就像是遊戲角色狀態,或是文書編輯器中的文字等。 **Memento** 保留 Originator 內部狀態 (資料) 的物件,例如遊戲中要存檔的資料。 **Caretaker** 主要功用是管理 Memento 物件。  Originator 通常是遊戲的主體class,要有回傳當前狀態的Memento class的method以及接收 Memento的class用於恢復狀態 Memento 儲存數據用,比如生命跟經驗值 Caretaker 保管Memento的class,小範例可以只用單一變數複雜的可以用list儲存多個Memento 為什麼要有Caretaker,因為單一原則且提升內聚力(cohesion) ## 區隔 Carataker 以及 Originator 為什麼不把復原的動作寫到 Originator 就好呢? Carataker 的工作是決定要在哪個時間點做快照、復原以及保留 Memento。 而 Originator 則是負責產生 Memento 以及利用傳遞過來的 Memento 復原自己的狀態。 Carataker 跟 Originator 有這樣的分配,好處是要做以下的修正時,可以不用去修改 Originator。 * 將復原動作修改為需要有一個步驟以上 * 除了復原之外,還要將目前的狀態儲存成檔案 ## 範例 ```java= public class Memento { int money; // 手邊金錢總額 Vector fruits; // 水果 public int getMoney() { // 取得手邊金錢總額(narrow interface),表示可影響內部狀態的比例較少,外界能做的不多 return money; } Memento(int money) { // 建構子(wide interface) this.money = money; this.fruits = new Vector(); } void addFruit(String fruit) { // 新增水果(wide interface) fruits.add(fruit); } Vector getFruits() { return (Vector)fruits.clone(); } } //Originator public class Gamer { private int money; // 手邊金錢總額 private Vector fruits = new Vector(); // 水果 private Random random = new Random(); // 亂數產生器 private static String[] fruitsname = { // 水果名稱一覽表 "蘋果", "葡萄", "香蕉", "橘子", }; public Gamer(int money) { // 建構子 this.money = money; } public int getMoney() { // 取得目前的手邊金錢總額 return money; } public void bet() { // 下注…開始遊戲 int dice = random.nextInt(6) + 1; // 擲骰子 if (dice == 1) { // 第1次…手邊金錢有變多 money += 100; System.out.println("手邊金錢增加了。"); } else if (dice == 2) { // 第2次…手邊金錢剩一半 money /= 2; System.out.println("手邊金錢剩一半。"); } else if (dice == 6) { // 第6次…得到水果 String f = getFruit(); System.out.println("得到水果(" + f + ")。"); fruits.add(f); } else { // 其他…沒有發生什麼事 System.out.println("沒有發生什麼事。"); } } public Memento createMemento() { // 快照存證(snapshot) Memento m = new Memento(money); Iterator it = fruits.iterator(); while (it.hasNext()) { String f = (String)it.next(); if (f.startsWith("好吃的")) { // 只儲存好吃的水果 m.addFruit(f); } } return m; } public void restoreMemento(Memento memento) { // 進行復原 this.money = memento.money; this.fruits = memento.getFruits(); } public String toString() { // 輸出成字串 return "[money = " + money + ", fruits = " + fruits + "]"; } private String getFruit() { // 得到1個水果 String prefix = ""; if (random.nextBoolean()) { prefix = "好吃的"; } return prefix + fruitsname[random.nextInt(fruitsname.length)]; } } //直接把main當Caretaker public class Main { public static void main(String[] args) { Gamer gamer = new Gamer(100); // 一開始手邊金錢總額為100 Memento memento = gamer.createMemento(); // 預先儲存最初狀態 for (int i = 0; i < 100; i++) { System.out.println("==== " + i); // 輸出次數 System.out.println("現況:" + gamer); // 輸出主人翁的目前狀態 gamer.bet(); // 進行遊戲 System.out.println("手邊金錢總額為" + gamer.getMoney() + "元。"); // 決定如何處理Memento if (gamer.getMoney() > memento.getMoney()) { System.out.println(" (因為已經贏了不少,故先儲存目前狀態)"); memento = gamer.createMemento(); } else if (gamer.getMoney() < memento.getMoney() / 2) { System.out.println(" (因為已經輸了很多,故恢復到前次狀態)"); gamer.restoreMemento(memento); } // 等候 try { Thread.sleep(1000); } catch (InterruptedException e) { } System.out.println(""); } } } ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up