成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

開(kāi)發(fā) 前端
面向?qū)ο蟪绦蛑校粋€(gè)類(lèi)在生命周期過(guò)程中,其屬性構(gòu)成的狀態(tài)是會(huì)不斷變化的。這種變化會(huì)帶來(lái)很多不確定性,尤其在多線程場(chǎng)景下,可能也會(huì)引發(fā)一些意想不到的問(wèn)題。

本篇講解Java設(shè)計(jì)模式中的備忘錄模式,分為定義、模式應(yīng)用前案例、結(jié)構(gòu)、模式應(yīng)用后案例、適用場(chǎng)景、模式可能存在的困惑和本質(zhì)探討7個(gè)部分。

定義

備忘錄模式是在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài),這樣以后就可以將該對(duì)象恢復(fù)到保存的狀態(tài)。

在新的分類(lèi)方式中,備忘錄模式被劃分至類(lèi)屬性相關(guān)需求類(lèi)別中,其應(yīng)對(duì)的是類(lèi)的狀態(tài)屬性需要恢復(fù)的要求。

模式應(yīng)用前案例

文本編輯器是一個(gè)備忘錄模式的典型應(yīng)用場(chǎng)景。接下來(lái),先來(lái)看一下未使用備忘錄模式之前的代碼實(shí)現(xiàn)。

public class TextEditor {//編輯器類(lèi)-直接實(shí)現(xiàn)保存和恢復(fù)操作
private String content;
private String previousContent;

public void write(String text) {
if(this.content == null ) {
this.content = "";
        }
this.content += text;
    }

// 保存當(dāng)前內(nèi)容為上一個(gè)版本的狀態(tài)
public void save() {
this.previousContent = this.content;
    }

// 恢復(fù)到上一個(gè)版本的狀態(tài)
public void undo(){
if(this.previousContent != null){
this.content = this.previousContent;
        }
    }

// 獲取內(nèi)容
public String getContent(){
return this.content;
    }

}

public class Client {//調(diào)用方代碼
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());
    }
}

在上述代碼中,主要問(wèn)題出現(xiàn)在TextEditor類(lèi)中。為了實(shí)現(xiàn)恢復(fù)到上一步這個(gè)操作,在類(lèi)中增加了previousContent屬性。

如果這個(gè)功能是后來(lái)才需要增加的,則違背了OCP開(kāi)閉原則。此外,如果后續(xù)要增加恢復(fù)上兩步的操作,是否還要新增一個(gè)doublepreviousContent屬性。顯然,對(duì)于這種類(lèi)狀態(tài)(或?qū)傩裕┯凶兓夷軌蚧謴?fù)的場(chǎng)景,應(yīng)該有更好的解決方案。

結(jié)構(gòu)

備忘錄模式的示例實(shí)現(xiàn)代碼如下。

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) {
// 創(chuàng)建Originator對(duì)象
        Originator originator = new Originator();

// 設(shè)置初始狀態(tài)
        originator.setState("State 1");
        System.out.println("Initial State: " + originator.getState());

// 創(chuàng)建Caretaker對(duì)象并保存?zhèn)渫?        Caretaker caretaker = new Caretaker();
        caretaker.setMemento(originator.createMemento());

// 改變Originator的狀態(tài)
        originator.setState("State 2");
        System.out.println("State after change: " + originator.getState());

// 恢復(fù)到之前保存的狀態(tài)
        originator.setMemento(caretaker.getMemento());
        System.out.println("State after restore: " + originator.getState());
    }
}

從備忘錄模式的結(jié)構(gòu)和示例代碼中,可以看到原有類(lèi)Originator僅保留了與自身核心業(yè)務(wù)功能相關(guān)的屬性,并將其需要恢復(fù)狀態(tài)的屬性state放在一個(gè)Memento類(lèi)中保存。

Originator增加了兩個(gè)比較簡(jiǎn)潔的方法,一個(gè)是創(chuàng)建Memento,一個(gè)是從Memento中恢復(fù),所以setMemento方法使用restoreFromMemento會(huì)更加準(zhǔn)確。

同時(shí),增加了一個(gè)Caretaker類(lèi),它用于保存、恢復(fù)Memento。是恢復(fù)到上一個(gè)狀態(tài)還是上兩個(gè)狀態(tài)都由Caretaker類(lèi)專(zhuān)門(mén)負(fù)責(zé)。

不難發(fā)現(xiàn),在備忘錄模式下,各個(gè)類(lèi)職責(zé)分工明確,核心類(lèi)Originator專(zhuān)注于核心業(yè)務(wù)功能,Memento和Caretaker兩個(gè)支撐類(lèi)則用于實(shí)現(xiàn)狀態(tài)的保存和恢復(fù)。

模式應(yīng)用后案例

上面文本編輯器的案例,在應(yīng)用備忘錄模式之后的代碼實(shí)現(xiàn)如下。

TextEditor類(lèi)刪掉了PreviousContent屬性,職責(zé)更加單一。

public class TextEditor {// 編輯器類(lèi)(Originator)- 負(fù)責(zé)創(chuàng)建備忘錄和恢復(fù)到之前狀態(tài)

private String content;

public void write(String text) {
if(this.content == null) {
this.content = "";
        }
this.content += text;
    }

// 創(chuàng)建當(dāng)前內(nèi)容對(duì)應(yīng)的備份
public EditorMemento createMemento(){
return new EditorMemento(this.content);
    }

// 從傳入Mememtor對(duì)象中獲取內(nèi)容并進(jìn)行還原
public void restoreFromMemento(EditorMemento memento){
this.content = memento.getContent();
    }

public String getContent() {
return this.content;
    }
}

增加EditorMemento和UndoManager兩個(gè)類(lèi),分別實(shí)現(xiàn)TextEditor中Content屬性的保存,以及EditorMemento的管理。

public class EditorMemento {// 備忘錄類(lèi)(Memento)- 存儲(chǔ)文本編輯器的狀態(tài)

private final String content;

public EditorMemento(String content) {
this.content = content;
    }

public String getContent() {
return this.content;
    }

}

public class UndoManager {// 管理者類(lèi)(Caretaker)-負(fù)責(zé)管理保存和恢復(fù)操作

    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;
    }
}

最后,調(diào)用方代碼如下。

public class Client {//調(diào)用方代碼

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());
    }
}

適用場(chǎng)景

備忘錄模式適用的場(chǎng)景非常明確,就是原有類(lèi)在生命周期變化過(guò)程中,其屬性的狀態(tài)還可能需要恢復(fù)的場(chǎng)景。

模式可能存在的困惑

困惑1:為什么要有Caretaker類(lèi),為什么不能在Memento或Originator中實(shí)現(xiàn)保存和恢復(fù)功能,這樣程序更加簡(jiǎn)潔?

如果在Originator中實(shí)現(xiàn),又違背了SRP單一職責(zé)和OCP開(kāi)閉原則;如果在Memento實(shí)現(xiàn),這個(gè)類(lèi)功能會(huì)變多,每次在Originator中創(chuàng)建Memento對(duì)象會(huì)占用更多內(nèi)存,從這個(gè)角度就不合適。

困惑2:Memento類(lèi)只是一個(gè)數(shù)據(jù)的封裝類(lèi),為什么Originator的狀態(tài)屬性不能直接放在Caretaker中通過(guò)一個(gè)數(shù)據(jù)屬性來(lái)實(shí)現(xiàn)?

實(shí)際上,許多人在考慮狀態(tài)恢復(fù)的策略時(shí),通常會(huì)優(yōu)先想到這個(gè)方案。為了更好地進(jìn)行說(shuō)明,這里將代碼實(shí)現(xiàn)羅列出來(lái)。

public class TextEditor {// 編輯器類(lèi)(Originator)- 負(fù)責(zé)創(chuàng)建備忘錄和恢復(fù)到之前狀態(tài)

private String content;

public void write(String text) {
if(this.content == null) {
this.content = "";
        }
this.content += text;
    }

// 創(chuàng)建當(dāng)前內(nèi)容對(duì)應(yīng)的備份
public void saveContent(){
        UndoManager.save(this.content);
    }

// 獲取內(nèi)容并進(jìn)行還原
public void restoreFromContent(){
this.content = UndoManager.undo();
    }

public String getContent() {
return this.content;
    }
}

public class UndoManager {// 管理者類(lèi)(Caretaker)-負(fù)責(zé)管理保存和恢復(fù)操作

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 {//調(diào)用方代碼

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());
    }
}

這種方式下,似乎實(shí)現(xiàn)起來(lái)更加簡(jiǎn)潔清晰。然而,缺點(diǎn)也比較明顯。TextEditor與UndoManager緊耦合的情況下,如果TextEditor要求也能夠?qū)崿F(xiàn)恢復(fù)到前兩個(gè)狀態(tài),此時(shí)UndoManager增加了一個(gè)undo2的方法,那么TextEditor也需要一并修改。

但是在備忘錄模式下,TextEditor相當(dāng)于至于純數(shù)據(jù)類(lèi)Memento進(jìn)行交互,面對(duì)上面的需求并不需要修改,只需要將上兩個(gè)的Memento傳參即可。

困惑3:在關(guān)于備忘錄模式的一些材料中,會(huì)看到寬接口和窄接口,具體是什么含義?

寬接口指的是Memento備忘錄對(duì)象提供給Originator訪問(wèn)其內(nèi)部狀態(tài)的全部信息,包括私有數(shù)據(jù)。因?yàn)镸emento里的數(shù)據(jù)其實(shí)就是Originator中要保存、恢復(fù)狀態(tài)的數(shù)據(jù),因此Originator需要能訪問(wèn)到具體的數(shù)據(jù)信息才可以。

窄接口指的是Memento備忘錄對(duì)象對(duì)Caretaker對(duì)象指提供必要的信息進(jìn)行訪問(wèn)和恢復(fù)操作。因?yàn)镃aretaker對(duì)象需要是是Memento對(duì)象自身,并不需要訪問(wèn)Memento中的數(shù)據(jù),因此稱(chēng)之為窄接口。

困惑4:備忘錄模式實(shí)現(xiàn)之后,對(duì)于調(diào)用方的交互似乎變得更加復(fù)雜?

一件事情往往有得必有失,很難做到兩全其美。為了使得Originator不違背SRP單一職責(zé)和OCP開(kāi)閉原則,Client只能增加交互。

如果在Client和備忘錄模式的類(lèi)之間增加一個(gè)中間代理類(lèi),這樣可以減少與調(diào)用方之間的交互,但是代價(jià)是又新增一個(gè)支撐類(lèi)。

本質(zhì)

面向?qū)ο蟪绦蛑校粋€(gè)類(lèi)在生命周期過(guò)程中,其屬性構(gòu)成的狀態(tài)是會(huì)不斷變化的。這種變化會(huì)帶來(lái)很多不確定性,尤其在多線程場(chǎng)景下,可能也會(huì)引發(fā)一些意想不到的問(wèn)題。因此,Java語(yǔ)言中經(jīng)常提倡要利用不變性、局部變量等應(yīng)對(duì)這種不確定性。

然而,在某些現(xiàn)實(shí)場(chǎng)景下,類(lèi)隨著時(shí)間不斷變化是有必要的,并且要求還能沿著時(shí)間向后回退。此時(shí),備忘錄提供了一種管理對(duì)象狀態(tài)的機(jī)制,并且讓原有對(duì)象維持良好的封裝性。

責(zé)任編輯:武曉燕 來(lái)源: 今日頭條
相關(guān)推薦

2024-05-13 10:45:25

中介模式面向?qū)ο?/a>數(shù)量

2024-05-10 08:43:04

外觀模式接口系統(tǒng)

2024-05-09 09:09:19

組合模式對(duì)象

2024-05-11 14:18:44

迭代器模式業(yè)務(wù)

2024-05-17 10:08:59

享元模式分類(lèi)方式

2019-08-27 14:46:59

ElasticSearES數(shù)據(jù)庫(kù)

2020-11-02 10:41:33

備忘錄模式

2020-07-10 08:03:35

DNS網(wǎng)絡(luò)ARPAne

2023-05-29 08:45:45

Java注解數(shù)據(jù)形式

2025-02-17 14:48:14

2023-10-07 00:14:53

2018-12-24 21:40:12

2023-03-13 08:12:37

Golang編程路徑問(wèn)題

2023-10-31 09:07:16

備忘錄模式保存

2017-03-21 11:02:59

基礎(chǔ)深度學(xué)習(xí)備忘錄

2011-04-11 10:03:32

錢(qián)伯斯思科

2011-08-16 18:38:23

Core Animat動(dòng)畫(huà)

2019-09-09 11:02:17

Nginx進(jìn)程模型

2014-04-17 10:30:41

Linux 命令黑白備忘錄

2023-10-10 15:26:30

內(nèi)存泄露OOM
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 中文字幕一区二区三区四区五区 | 国产 日韩 欧美 制服 另类 | 欧美.com| 精品一区国产 | 人人澡人人爱 | 精品av | 91精品久久| 成人日批视频 | 亚洲精品久 | 亚洲精品乱码久久久久久9色 | 日韩在线成人 | 久产久精国产品 | 日韩中文一区二区三区 | a级黄色网 | 欧美久久视频 | 精品一区二区观看 | 91国内在线观看 | 日韩午夜一区二区三区 | 福利视频网址 | 盗摄精品av一区二区三区 | 亚洲三区在线观看 | 天堂在线中文字幕 | 成人免费观看男女羞羞视频 | 欧美精品一区在线 | www.四虎.com | 久久精品亚洲一区二区三区浴池 | yiren22综合网成人 | 久久69精品久久久久久久电影好 | 久久亚洲一区二区三区四区 | 久久人人网 | 亚洲欧美高清 | 国产精品一区在线 | 高清视频一区二区三区 | 亚洲成人精品在线 | 伊人网综合 | 国产精品毛片av | 成人免费一区二区三区牛牛 | 四季久久免费一区二区三区四区 | 国产日韩欧美在线观看 | 澳门永久av免费网站 | www.亚洲精品 |