1. 程式人生 > 實用技巧 >行為型模式之備忘錄模式

行為型模式之備忘錄模式

目錄

每個人都有犯錯誤的時候,都希望有種“後悔藥”能彌補自己的過失,讓自己重新開始,但現實是殘酷的。在計算機應用中,客戶同樣會常常犯錯誤,能否提供“後悔藥”給他們呢?當然是可以的,而且是有必要的。這個功能由“備忘錄模式”來實現。

其實很多應用軟體都提供了這項功能,如 Word、記事本、Photoshop、Eclipse 等軟體在編輯時按 Ctrl+Z 組合鍵時能撤銷當前操作,使文件恢復到之前的狀態;還有在 IE 中的後退鍵、資料庫事務管理中的回滾操作、玩遊戲時的中間結果存檔功能、資料庫與作業系統的備份操作、棋類遊戲中的悔棋功能等都屬於這類。

備忘錄模式能記錄一個物件的內部狀態,當用戶後悔時能撤銷當前操作,使資料恢復到它原先的狀態。

定義與特點

備忘錄(Memento)模式的定義:在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態,以便以後當需要時能將該物件恢復到原先儲存的狀態,該模式又叫快照模式

備忘錄模式是一種物件行為型模式,其主要優點如下:

  • 提供了一種可以恢復狀態的機制:當用戶需要時能夠比較方便地將資料恢復到某個歷史的狀態。
  • 實現了內部狀態的封裝:除了建立它的發起人之外,其他物件都不能夠訪問這些狀態資訊。
  • 簡化了發起人類:發起人不需要管理和儲存其內部狀態的各個備份,所有狀態資訊都儲存在備忘錄中,並由管理者進行管理,這符合單一職責原則。

其主要缺點是:資源消耗大,如果要儲存的內部狀態資訊過多或者特別頻繁,將會佔用比較大的記憶體資源。

結構與實現

備忘錄模式的核心是設計備忘錄類以及用於管理備忘錄的管理者類,現在我們來學習其結構與實現。

模式的結構

備忘錄模式的主要角色如下:

  • 發起人(Originator)角色:記錄當前時刻的內部狀態資訊,提供建立備忘錄和恢復備忘錄資料的功能,實現其他業務功能,它可以訪問備忘錄裡的所有資訊。
  • 備忘錄(Memento)角色:負責儲存發起人的內部狀態,在需要的時候提供這些內部狀態給發起人。
  • 管理者(Caretaker)角色:對備忘錄進行管理,提供儲存與獲取備忘錄的功能,但其不能對備忘錄的內容進行訪問與修改。

備忘錄模式的結構圖如圖所示:

模式的實現

備忘錄模式的實現程式碼如下:

class Program
{
    static void Main(string[] args)
    {
        Originator or = new Originator();
        Caretaker cr = new Caretaker();
        or.SetState("S0");
        Console.WriteLine("初始狀態:" + or.GetState());
        cr.SetMemento(or.CreateMemento()); //儲存狀態      
        or.SetState("S1");        
        Console.WriteLine("新的狀態:" + or.GetState());
        or.RestoreMemento(cr.GetMemento()); //恢復狀態
        Console.WriteLine("恢復狀態:" + or.GetState());        
        Console.Read();        
    }
}

//備忘錄
public class Memento
{ 
    private String state; 
    public Memento(String state)
    { 
        this.state=state; 
    }     
    public void SetState(String state)
    { 
        this.state=state; 
    }
    public String GetState()
    { 
        return state; 
    }
}

//發起人
public class Originator
{ 
    private String state;     
    public void SetState(String state)
    { 
        this.state=state; 
    }
    public String GetState()
    { 
        return state; 
    }
    public Memento CreateMemento()
    { 
        return new Memento(state); 
    }
    public void RestoreMemento(Memento memento)
    {
        this.SetState(memento.GetState()); 
    } 
}

//管理者
public class Caretaker
{ 
    private Memento memento;       
    public void SetMemento(Memento memento)
    { 
        this.memento=memento; 
    }
    public Memento GetMemento()
    { 
        return memento; 
    }
}

程式執行的結果如下:

初始狀態:S0
新的狀態:S1
恢復狀態:S0

應用場景

前面學習了備忘錄模式的定義與特點、結構與實現,現在來看該模式的以下應用場景:

  • 需要儲存與恢復資料的場景,如玩遊戲時的中間結果的存檔功能。
  • 需要提供一個可回滾操作的場景,如 Word、記事本、Photoshop,Eclipse 等軟體在編輯時按 Ctrl+Z 組合鍵,還有資料庫中事務操作。

擴充套件:同原型模式混合使用

在備忘錄模式中,通過定義“備忘錄”來備份“發起人”的資訊,而原型模式的 clone() 方法具有自備份功能。所以,如果讓發起人實現 Cloneable 介面就有備份自己的功能,這時可以刪除備忘錄類,其結構圖如圖所示:

實現程式碼如下:

class Program
{
    static void Main(string[] args)
    {
        OriginatorPrototype or=new OriginatorPrototype();
        PrototypeCaretaker cr=new PrototypeCaretaker();       
        or.SetState("S0"); 
        Console.WriteLine("初始狀態:"+or.GetState());           
        cr.SetMemento(or.CreateMemento()); //儲存狀態      
        or.SetState("S1"); 
        Console.WriteLine("新的狀態:"+or.GetState());        
        or.RestoreMemento(cr.GetMemento()); //恢復狀態
        Console.WriteLine("恢復狀態:"+or.GetState());      
        Console.Read();        
    }
}

//發起人原型
class OriginatorPrototype  : ICloneable
{ 
    private String state;     
    public void SetState(String state)
    { 
        this.state=state; 
    }
    public String GetState()
    { 
        return state; 
    }
    public OriginatorPrototype CreateMemento()
    {
        return (OriginatorPrototype)this.Clone(); 
    } 
    public void RestoreMemento(OriginatorPrototype opt)
    { 
        this.SetState(opt.GetState()); 
    }
    public object Clone()
    {
        try
        {
            return base.MemberwiseClone();
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        return null;
    }
}
//原型管理者
class PrototypeCaretaker
{ 
    private OriginatorPrototype opt;       
    public void SetMemento(OriginatorPrototype opt)
    { 
        this.opt=opt; 
    }
    public OriginatorPrototype GetMemento()
    { 
        return opt; 
    }
}

程式的執行結果如下:

初始狀態:S0
新的狀態:S1
恢復狀態:S0