折騰Java設計模式之備忘錄模式

大萌小路發表於2019-04-09

原文地址:折騰Java設計模式之備忘錄模式

備忘錄模式

Without violating encapsulation, capture and externalize an object's internal state allowing the object to be restored to this state later.

在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。

所謂備忘錄模式就是在不破壞封裝的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態,這樣可以在以後將物件恢復到原先儲存的狀態。很多時候我們總是需要記錄一個物件的內部狀態,這樣做的目的就是為了允許使用者取消不確定或者錯誤的操作,能夠恢復到他原先的狀態,使得他有"後悔藥"可吃。通過一個備忘錄類專門儲存物件狀態。客戶不與備忘錄類耦合,與備忘錄管理類耦合。

備忘錄模式UML

uml

UML類圖

Caretaker類是指用於儲存(createMemento())和還原(restore(memento))發起方內部狀態的Originator類。 發起方類實現 (1)createMemento(),通過建立和返回儲存發起方當前內部狀態的memento物件 (2)通過從傳入的memento物件還原狀態來還原(memento)。

UML時序圖

(1)儲存發起人的內部狀態:Caretaker對Originator呼叫createMemento(),建立memento物件,儲存其當前內部狀態(setState()),並將memento返回給Caretaker。

(2)恢復發起人的內部狀態:Caretaker對Originator呼叫restore(memento),並指定儲存應恢復狀態的memento物件。發起者從memento獲取狀態(getState())以設定其自己的狀態。

備忘錄模式角色結構

(1) 備忘錄(Memento)角色:備忘錄角色儲存“備忘發起角色”的內部狀態。“備忘發起角色”根據需要決定備忘錄角色儲存“備忘發起角色”的哪些內部狀態。為了防止“備忘發起角色”以外的其他物件訪問備忘錄。備忘錄實際上有兩個介面,“備忘錄管理者角色”只能看到備忘錄提供的窄介面——對於備忘錄角色中存放的屬性是不可見的。“備忘發起角色”則能夠看到一個寬介面——能夠得到自己放入備忘錄角色中屬性。

(2) 備忘發起(Originator)角色:“備忘發起角色”建立一個備忘錄,用以記錄當前時刻它的內部狀態。在需要時使用備忘錄恢復內部狀態。

(3) 備忘錄管理者(Caretaker)角色:負責儲存好備忘錄。不能對備忘錄的內容進行操作或檢查。

乾貨示例

原始碼地址

public class Caretaker {

​    public static void main(String[] args) {
​        List<Memento> savedStates = new ArrayList();
​        Originator originator = new Originator();
​        originator.set("State1");
​        originator.set("State2");
​        savedStates.add(originator.saveToMemento());

​        originator.set("State3");
​        savedStates.add(originator.saveToMemento());

​        originator.set("State4");
​        originator.restoreFromMemento(savedStates.get(1));
​    }
}


@Slf4j
public class Originator {

​    private String state;

​    //狀態更改public void set(String state) {
​        this.state = state;
​        log.info("Originator: Setting state to {}", state);
​    }

​    //將狀態儲存到備忘錄裡public Memento saveToMemento() {
​        log.info("Originator: Saving to Memento.");
​        return new Memento(this.state);
​    }

​    //從備忘錄裡取出狀態並回滾public void restoreFromMemento(Memento memento) {
​        this.state = memento.getState();
​        log.info("Originator: State after restoring from Memento: {}", state);
​    }
}


@Data
@AllArgsConstructor
public class Memento {

​    //狀態維護private String state;

}

複製程式碼

示例結果

image-20190126161658817

從上述程式碼中看的出,隨著狀態變更,用List維護髮起者的狀態列表,從備忘錄中取出狀態以便回退狀態。

java中的使用

生成物件狀態的一個快照,以便物件可以恢復原始狀態而不用暴露自身的內容。Date物件通過自身內部的一個long值來實現備忘錄模式。

java.util.Date

java.io.Serializable

總結

優點

1、給使用者提供了一種可以恢復狀態的機制,可以使使用者能夠比較方便地回到某個歷史的狀態。 2、實現了資訊的封裝,使得使用者不需要關心狀態的儲存細節。

缺點

消耗資源。如果類的成員變數過多,勢必會佔用比較大的資源,而且每一次儲存都會消耗一定的記憶體。

使用場景

1、需要儲存/恢復資料的相關狀態場景。 2、提供一個可回滾的操作。

注意事項

1、為了符合迪米特原則,還要增加一個管理備忘錄的類。 2、為了節約記憶體,可使用原型模式+備忘錄模式。

參考

備忘錄模式|菜鳥教程

Memento pattern

細數JDK裡的設計模式

歡迎關注

微信公眾號

相關文章