日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

一文徹底搞明白備忘錄模式

來源: 責編: 時間:2024-05-16 09:02:28 138觀看
導讀本篇講解Java設計模式中的備忘錄模式,分為定義、模式應用前案例、結構、模式應用后案例、適用場景、模式可能存在的困惑和本質探討7個部分。定義備忘錄模式是在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象

本篇講解Java設計模式中的備忘錄模式,分為定義、模式應用前案例、結構、模式應用后案例、適用場景、模式可能存在的困惑和本質探討7個部分。N0m28資訊網——每日最新資訊28at.com

定義

備忘錄模式是在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態,這樣以后就可以將該對象恢復到保存的狀態。N0m28資訊網——每日最新資訊28at.com

在新的分類方式中,備忘錄模式被劃分至類屬性相關需求類別中,其應對的是類的狀態屬性需要恢復的要求。N0m28資訊網——每日最新資訊28at.com

N0m28資訊網——每日最新資訊28at.com

模式應用前案例

文本編輯器是一個備忘錄模式的典型應用場景。接下來,先來看一下未使用備忘錄模式之前的代碼實現。N0m28資訊網——每日最新資訊28at.com

public class TextEditor {//編輯器類-直接實現保存和恢復操作private String content;private String previousContent;public void write(String text) {if(this.content == null ) {this.content = "";        }this.content += text;    }// 保存當前內容為上一個版本的狀態public void save() {this.previousContent = this.content;    }// 恢復到上一個版本的狀態public void undo(){if(this.previousContent != null){this.content = this.previousContent;        }    }// 獲取內容public String getContent(){return this.content;    }}public class Client {//調用方代碼public static void main(String[] ars){        TextEditor editor=new TextEditor();        editor.write("Hello, ");        System.out.println(editor.getContent());        editor.save();        editor.write("World!");        System.out.println(editor.getContent());        editor.undo();        System.out.println(editor.getContent());    }}

在上述代碼中,主要問題出現在TextEditor類中。為了實現恢復到上一步這個操作,在類中增加了previousContent屬性。N0m28資訊網——每日最新資訊28at.com

如果這個功能是后來才需要增加的,則違背了OCP開閉原則。此外,如果后續要增加恢復上兩步的操作,是否還要新增一個doublepreviousContent屬性。顯然,對于這種類狀態(或屬性)有變化且能夠恢復的場景,應該有更好的解決方案。N0m28資訊網——每日最新資訊28at.com

結構

N0m28資訊網——每日最新資訊28at.com

備忘錄模式的示例實現代碼如下。N0m28資訊網——每日最新資訊28at.com

public class Originator {private String state;public Memento createMemento() {return new Memento(state);    }public void setMemento(Memento memento) {this.state = ((Memento) memento).getState();    }public String getState() {return state;    }public void setState(String state) {this.state = state;    }}public class Memento{private final String state;public Memento(String state) {this.state = state;    }public String getState() {return state;    }}public class Caretaker {private Memento memento;public void setMemento(Memento memento) {this.memento = memento;    }public Memento getMemento() {return memento;    }}public class Client {public static void main(String[] args) {// 創建Originator對象        Originator originator = new Originator();// 設置初始狀態        originator.setState("State 1");        System.out.println("Initial State: " + originator.getState());// 創建Caretaker對象并保存備忘錄        Caretaker caretaker = new Caretaker();        caretaker.setMemento(originator.createMemento());// 改變Originator的狀態        originator.setState("State 2");        System.out.println("State after change: " + originator.getState());// 恢復到之前保存的狀態        originator.setMemento(caretaker.getMemento());        System.out.println("State after restore: " + originator.getState());    }}

從備忘錄模式的結構和示例代碼中,可以看到原有類Originator僅保留了與自身核心業務功能相關的屬性,并將其需要恢復狀態的屬性state放在一個Memento類中保存。N0m28資訊網——每日最新資訊28at.com

Originator增加了兩個比較簡潔的方法,一個是創建Memento,一個是從Memento中恢復,所以setMemento方法使用restoreFromMemento會更加準確。N0m28資訊網——每日最新資訊28at.com

同時,增加了一個Caretaker類,它用于保存、恢復Memento。是恢復到上一個狀態還是上兩個狀態都由Caretaker類專門負責。N0m28資訊網——每日最新資訊28at.com

不難發現,在備忘錄模式下,各個類職責分工明確,核心類Originator專注于核心業務功能,Memento和Caretaker兩個支撐類則用于實現狀態的保存和恢復。N0m28資訊網——每日最新資訊28at.com

模式應用后案例

上面文本編輯器的案例,在應用備忘錄模式之后的代碼實現如下。N0m28資訊網——每日最新資訊28at.com

TextEditor類刪掉了PreviousContent屬性,職責更加單一。N0m28資訊網——每日最新資訊28at.com

public class TextEditor {// 編輯器類(Originator)- 負責創建備忘錄和恢復到之前狀態private String content;public void write(String text) {if(this.content == null) {this.content = "";        }this.content += text;    }// 創建當前內容對應的備份public EditorMemento createMemento(){return new EditorMemento(this.content);    }// 從傳入Mememtor對象中獲取內容并進行還原public void restoreFromMemento(EditorMemento memento){this.content = memento.getContent();    }public String getContent() {return this.content;    }}

增加EditorMemento和UndoManager兩個類,分別實現TextEditor中Content屬性的保存,以及EditorMemento的管理。N0m28資訊網——每日最新資訊28at.com

public class EditorMemento {// 備忘錄類(Memento)- 存儲文本編輯器的狀態private final String content;public EditorMemento(String content) {this.content = content;    }public String getContent() {return this.content;    }}public class UndoManager {// 管理者類(Caretaker)-負責管理保存和恢復操作    Stack<EditorMemento> emStack =new Stack<>();public void save(EditorMemento memento){this.emStack.push(memento);    }public EditorMemento undo(){if(!this.emStack.empty()){return this.emStack.pop();        }return null;    }}

最后,調用方代碼如下。N0m28資訊網——每日最新資訊28at.com

public class Client {//調用方代碼public static void main(String[] ars){        TextEditor editor = new TextEditor();        UndoManager undoManager=new UndoManager();        editor.write("Hello, ");        undoManager.save(editor.createMemento());        editor.write("World!");//undoManager.save(editor.createMemento());        System.out.println(editor.getContent());        editor.restoreFromMemento(undoManager.undo());        System.out.println(editor.getContent());    }}

適用場景

備忘錄模式適用的場景非常明確,就是原有類在生命周期變化過程中,其屬性的狀態還可能需要恢復的場景。N0m28資訊網——每日最新資訊28at.com

模式可能存在的困惑

困惑1:為什么要有Caretaker類,為什么不能在Memento或Originator中實現保存和恢復功能,這樣程序更加簡潔?N0m28資訊網——每日最新資訊28at.com

如果在Originator中實現,又違背了SRP單一職責和OCP開閉原則;如果在Memento實現,這個類功能會變多,每次在Originator中創建Memento對象會占用更多內存,從這個角度就不合適。N0m28資訊網——每日最新資訊28at.com

困惑2:Memento類只是一個數據的封裝類,為什么Originator的狀態屬性不能直接放在Caretaker中通過一個數據屬性來實現?N0m28資訊網——每日最新資訊28at.com

實際上,許多人在考慮狀態恢復的策略時,通常會優先想到這個方案。為了更好地進行說明,這里將代碼實現羅列出來。N0m28資訊網——每日最新資訊28at.com

public class TextEditor {// 編輯器類(Originator)- 負責創建備忘錄和恢復到之前狀態private String content;public void write(String text) {if(this.content == null) {this.content = "";        }this.content += text;    }// 創建當前內容對應的備份public void saveContent(){        UndoManager.save(this.content);    }// 獲取內容并進行還原public void restoreFromContent(){this.content = UndoManager.undo();    }public String getContent() {return this.content;    }}public class UndoManager {// 管理者類(Caretaker)-負責管理保存和恢復操作private static final Stack<String> emStack =new Stack<>();public static void save(String content){        emStack.push(content);    }public static String undo(){if(!emStack.empty()){return emStack.pop();        }return null;    }}public class Client {//調用方代碼public static void main(String[] ars){        TextEditor editor = new TextEditor();        editor.write("Hello, ");        editor.saveContent();        editor.write("World!");        System.out.println(editor.getContent());        editor.restoreFromContent();        System.out.println(editor.getContent());    }}

這種方式下,似乎實現起來更加簡潔清晰。然而,缺點也比較明顯。TextEditor與UndoManager緊耦合的情況下,如果TextEditor要求也能夠實現恢復到前兩個狀態,此時UndoManager增加了一個undo2的方法,那么TextEditor也需要一并修改。N0m28資訊網——每日最新資訊28at.com

但是在備忘錄模式下,TextEditor相當于至于純數據類Memento進行交互,面對上面的需求并不需要修改,只需要將上兩個的Memento傳參即可。N0m28資訊網——每日最新資訊28at.com

困惑3:在關于備忘錄模式的一些材料中,會看到寬接口和窄接口,具體是什么含義?N0m28資訊網——每日最新資訊28at.com

寬接口指的是Memento備忘錄對象提供給Originator訪問其內部狀態的全部信息,包括私有數據。因為Memento里的數據其實就是Originator中要保存、恢復狀態的數據,因此Originator需要能訪問到具體的數據信息才可以。N0m28資訊網——每日最新資訊28at.com

窄接口指的是Memento備忘錄對象對Caretaker對象指提供必要的信息進行訪問和恢復操作。因為Caretaker對象需要是是Memento對象自身,并不需要訪問Memento中的數據,因此稱之為窄接口。N0m28資訊網——每日最新資訊28at.com

困惑4:備忘錄模式實現之后,對于調用方的交互似乎變得更加復雜?N0m28資訊網——每日最新資訊28at.com

一件事情往往有得必有失,很難做到兩全其美。為了使得Originator不違背SRP單一職責和OCP開閉原則,Client只能增加交互。N0m28資訊網——每日最新資訊28at.com

如果在Client和備忘錄模式的類之間增加一個中間代理類,這樣可以減少與調用方之間的交互,但是代價是又新增一個支撐類。N0m28資訊網——每日最新資訊28at.com

本質

面向對象程序中,一個類在生命周期過程中,其屬性構成的狀態是會不斷變化的。這種變化會帶來很多不確定性,尤其在多線程場景下,可能也會引發一些意想不到的問題。因此,Java語言中經常提倡要利用不變性、局部變量等應對這種不確定性。N0m28資訊網——每日最新資訊28at.com

然而,在某些現實場景下,類隨著時間不斷變化是有必要的,并且要求還能沿著時間向后回退。此時,備忘錄提供了一種管理對象狀態的機制,并且讓原有對象維持良好的封裝性。N0m28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-88318-0.html一文徹底搞明白備忘錄模式

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 你不知道的JavaScript—探索 JavaScript 對象與原型

下一篇: Redis Zset詳解:排行榜絕佳選擇

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 左云县| 金华市| 阳信县| 孝感市| 雷波县| 铜山县| 盐津县| 沅江市| 竹山县| 天台县| 虞城县| 九龙坡区| 奈曼旗| 精河县| 五大连池市| 金华市| 永宁县| 连山| 华安县| 宁德市| 中方县| 米泉市| 同心县| 新化县| 平度市| 阳春市| 彭水| 炎陵县| 常宁市| 惠安县| 江永县| 银川市| 泽库县| 石渠县| 花莲市| 卓资县| 万载县| 莱芜市| 农安县| 汽车| 潍坊市|