設計模式-備忘錄模式

1. 備忘錄模式概述

備忘錄模式提供一種狀態恢復的實現機制, 用戶能夠方便的回到一個特定的歷史步驟, 當新的狀態無效或者存在問題時候, 可使用暫時存起來的備忘錄將狀態復原, 當前不少軟件中提供的撤銷操做, 其中使用了備忘錄模式.數據庫

備忘錄模式: 在不破壞封裝的前提下, 捕獲一個對象的內部狀態, 並在該對象以外保存這個狀態, 這樣能夠在之後將狀態恢復到原先保存的狀態. 它是一種對象行爲模式.編程

備忘錄模式的核心是備忘錄類以及用於管理備忘錄的負責人類的設計, 其結構以下所示:瀏覽器

  • 原發器(Originator): 它是一個普通類, 能夠建立一個備忘錄, 並存儲它的當前內部狀態, 也可使用備忘錄來恢復其內部狀態, 通常將須要保存內部狀態的類設計爲原發器.
  • 備忘錄(Memento): 存儲原發器的內部狀態, 根據原發器來決定保存那些內部狀態. 備忘錄的設計通常能夠參考原發器的設計, 根據實際須要肯定備忘錄中的屬性. 須要注意的是除了原發器自己和負責人以外, 備忘錄對象不能直接提供其它類使用, 原發器的設計在不一樣的編程語言中實現機制會有所不一樣.
  • 負責人(Caretaker): 負責人又稱爲管理者, 它負責保存備忘錄, 可是不能對備忘錄的內容進行檢查和操做. 在負責人類中有一個或者多個備忘錄對象, 它只負責存儲對象, 而不能修改對象, 也無需知道對象的實現細節.

備忘錄模式關鍵在於如何設計備忘錄類和負責人類. 因爲在備忘錄中存儲的是原發器的中間狀態, 所以須要防止原發器之外的其它對象訪問備忘錄, 特別是不容許其它對象來修改備忘錄.app

2. 中國象棋例子的Swift實現

備忘錄模式, Swift版本客戶端代碼:編程語言

備忘錄模式代碼:.net

// ChessMan
class ChessMan: NSObject {
    
    var label: String?
    var x: Int?
    var y: Int?
    
    // 初始化棋子狀態
    init(label: String, x: Int, y: Int) {
        
        self.label = label
        self.x = x
        self.y = y
    }
    
    // 移動棋子
    func move(x: Int, y: Int) {
        
        self.x = x
        self.y = y
    }
    
    
    // 保存狀態, 返回當前對象狀態的類, 即備忘錄
    func save() -> ChessmanMemento {
        return ChessmanMemento(chessman: self)
    }
    
    // 恢復到一個歷史狀態, 參數是一個狀態
    func restore(memento: ChessmanMemento) {
        
        label = memento.label
        x = memento.x
        y = memento.y
    }
}





// ChessmanMemento
class ChessmanMemento: NSObject {
    
    var label: String
    var x: Int
    var y: Int
    
    init(chessman: ChessMan) {
        
        label = chessman.label ?? ""
        x = chessman.x ?? 0
        y = chessman.y ?? 0
    }

}





// MementoCaretaker
class MementoCaretaker: NSObject {
    
    lazy var mementoArray = [ChessmanMemento]()
    
    
    // 根據歷史狀態, 從狀態列表中獲取一個歷史狀態
    func getMemento(index: Int) -> ChessmanMemento {
        return mementoArray.remove(at: index)
    }
    
    // 獲取最近的狀態
    func getLatestMemento() -> ChessmanMemento {
        
        return mementoArray.removeLast()
    }
    
    // 獲取最老的的狀態
    func getOldestMemento() -> ChessmanMemento {
        
        return mementoArray.removeFirst()
    }
    
    // 添加一個狀態
    func addMemento(memento: ChessmanMemento) {
        mementoArray.append(memento)
    }

}

3. 備忘錄模式的封裝

備忘錄是一個很特殊的對象,只有原發器對它擁有控制的權力,負責人只負責管理,而其餘類沒法訪問到備忘錄,所以咱們須要對備忘錄進行封裝。設計

爲了實現對備忘錄對象的封裝,須要對備忘錄的調用進行控制,對於原發器而言,它能夠調用備忘錄的全部信息,容許原發器訪問返回到先前狀態所需的全部數據;對於負責人而言,只負責備忘錄的保存並將備忘錄傳遞給其餘對象;對於其餘對象而言,只須要從負責人處取出備忘錄對象並將原發器對象的狀態恢復,而無須關心備忘錄的保存細節。理想的狀況是隻容許生成該備忘錄的那個原發器訪問備忘錄的內部狀態。rest

在實際開發中,原發器與備忘錄之間的關係是很是特殊的,它們要分享信息而不讓其餘類知道,實現的方法因編程語言的不一樣而有所差別,在C++中可使用friend關鍵字,讓原發器類和備忘錄類成爲友元類,互相之間能夠訪問對象的一些私有的屬性;在Java語言中能夠將原發器類和備忘錄類放在一個包中,讓它們之間知足默認的包內可見性,也能夠將備忘錄類做爲原發器類的內部類,使得只有原發器才能夠訪問備忘錄中的數據,其餘對象都沒法使用備忘錄中的數據。code

4. 備忘錄模式總結

備忘錄模式在不少軟件的使用過程當中廣泛存在,可是在應用軟件開發中,它的使用頻率並不過高,由於如今不少基於窗體和瀏覽器的應用軟件並無提供撤銷操做。若是須要爲軟件提供撤銷功能,備忘錄模式無疑是一種很好的解決方案。在一些字處理軟件、圖像編輯軟件、數據庫管理系統等軟件中備忘錄模式都獲得了很好的應用。對象

4.1 主要優勢

  1. 它提供了一種狀態恢復的實現機制,使得用戶能夠方便地回到一個特定的歷史步驟,當新的狀態無效或者存在問題時,可使用暫時存儲起來的備忘錄將狀態復原。
  2. 備忘錄實現了對信息的封裝,一個備忘錄對象是一種原發器對象狀態的表示,不會被其餘代碼所改動。備忘錄保存了原發器的狀態,採用列表、堆棧等集合來存儲備忘錄對象能夠實現屢次撤銷操做。

4.2 主要缺點

資源消耗過大,若是須要保存的原發器類的成員變量太多,就不可避免須要佔用大量的存儲空間,每保存一次對象的狀態都須要消耗必定的系統資源。

4.3 適用場景

  1. 保存一個對象在某一個時刻的所有狀態或部分狀態,這樣之後須要時它可以恢復到先前的狀態,實現撤銷操做。
  2. 防止外界對象破壞一個對象歷史狀態的封裝性,避免將對象歷史狀態的實現細節暴露給外界對象。

Reference: http://blog.csdn.net/lovelion/article/details/7526747

相關文章
相關標籤/搜索