設(shè)計(jì)模式-備忘錄模式

備忘錄模式是使用一個(gè)備忘錄對(duì)象把另外一個(gè)對(duì)象內(nèi)部狀態(tài)進(jìn)行保存,在適當(dāng)?shù)臅r(shí)候還原到某個(gè)狀態(tài)。如同我們記錄某件事件,在需要回憶的時(shí)候去看下記事本。

先來(lái)看下類圖

該模式涉及到3個(gè)角色:

  • 發(fā)起人角色:Originator,該角色包含備忘錄對(duì)象,備忘錄對(duì)象存儲(chǔ)了他的狀態(tài);
  • 負(fù)責(zé)人角色:Caretaker,該角色保存?zhèn)渫泴?duì)象,但不檢查備忘錄對(duì)象內(nèi)容;
  • 備忘錄角色:Memento,將發(fā)起人對(duì)象的狀態(tài)保存起來(lái),,保護(hù)發(fā)起人的內(nèi)容不被外界訪問(wèn)

寬接口與白箱

備忘錄角色對(duì)如何其他對(duì)象提供一個(gè)接口,也就是寬接口的話,那么備忘錄角色存儲(chǔ)的內(nèi)部狀態(tài)都暴露給其他對(duì)象。這種情況導(dǎo)致發(fā)起人的狀態(tài)都沒(méi)看到,是破壞封裝性的,只能通過(guò)程序猿的自律。先來(lái)看下寬接口。

接下來(lái)看下代碼實(shí)現(xiàn):

public class Memento {

    private String state;
    
    public Memento(String state) {
        this.state=state;
    }

    public String getState() {
        return this.state;
    }

    public void setState(String state){
        this.state=state;
    }
    
}

上面這個(gè)備忘錄對(duì)象提供:1、對(duì)發(fā)起人的狀態(tài)進(jìn)行保存;2、可以訪問(wèn)的狀態(tài)是公開的;

//發(fā)起人
public class Originator {

    private String state;
    
    //創(chuàng)建備忘錄對(duì)象來(lái)保存狀態(tài)
    public Memento createMemento(){
        return new Memento(state);
    }
    
    //從備忘錄對(duì)象里面恢復(fù)狀態(tài)
    public void restoreMemento(Memento m){
        this.state=m.getState();
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        System.out.println("當(dāng)前狀態(tài):"+state);
        this.state = state;
    }
}

發(fā)起人對(duì)象利用創(chuàng)建一個(gè)新的備忘錄對(duì)象保存自己的狀態(tài);

//負(fù)責(zé)人
public class Caretaker {

    private Memento memento;
    
    public Memento retrieveMemento(){
        return this.memento;
    }
    
    public void saveMemento(Memento m){
        this.memento=m;
    }
    
}

負(fù)責(zé)人只負(fù)責(zé)保存?zhèn)渫泴?duì)象,對(duì)備忘錄對(duì)象里面的內(nèi)容不再變化??蛻舳说牟僮魅缦?/p>

public class Client {

    public static void main(String[] args) {
        Originator o=new Originator();
        Caretaker c=new Caretaker();
        
        //發(fā)起人狀態(tài)改變
        o.setState("Start");
        //負(fù)責(zé)人保存這個(gè)備忘錄
        c.saveMemento(o.createMemento());
        //改變狀態(tài)
        o.setState("End");
        //回到初始
        o.restoreMemento(c.retrieveMemento());

    }

}

/**  ---- result ----

當(dāng)前狀態(tài):Start
當(dāng)前狀態(tài):End
*/

上面就是白箱操作,很簡(jiǎn)單,但是破壞了對(duì)發(fā)起人的狀態(tài)封裝;

雙重接口

所謂雙重接口就是對(duì)某一個(gè)對(duì)象提供寬接口,對(duì)另外一些類提供窄接口。系統(tǒng)中可能需要將某個(gè)對(duì)象的狀態(tài)保存起來(lái),在某個(gè)時(shí)候進(jìn)行恢復(fù),但這些狀態(tài)并不希望被外界訪問(wèn),以免有外界直接修改狀態(tài)的危險(xiǎn),這個(gè)時(shí)候,備忘錄模式就很好的解決這個(gè)問(wèn)題,他利用寬接口和窄接口來(lái)保證。

假設(shè)窄接口對(duì)所有類公開,而公開類只對(duì)某一個(gè)公開,這個(gè)時(shí)候,我們可以把實(shí)現(xiàn)了寬接口和窄接口的具體類作為這個(gè)特殊類的內(nèi)部類,寬接口的方法也可以移植到這個(gè)特殊類上,而具體類里面的方法都是私有,這樣對(duì)特殊類可以訪問(wèn)所有接口,其他類智能調(diào)用特殊類的某些公開方法。有點(diǎn)饒人,畫個(gè)圖



圖1是一個(gè)基本樣子,進(jìn)行演變,首先寬接口方法歸屬到具體類里面,變成下面這個(gè)樣子



這樣寬接口的方法還是公開的,此時(shí)把具體類作為特殊類的內(nèi)部類,并且,把里面的方法都設(shè)置私有

這個(gè)時(shí)候的特殊類,也就發(fā)起者代碼如下:

public class DoubleInterfaceDemo {
    
    public static void main(String[] args) {
        DoubleInterfaceDemo demo=new DoubleInterfaceDemo();
        demo.new ConcreteCLass().wide();
        demo.new ConcreteCLass().getConcrete().does();
    }

    class ConcreteCLass implements Narrow{

        @Override
        public void does() {
            System.out.println("窄接口");
        }
        
        //這個(gè)接口只能DoubleInterfaceDemo自己用了
        private void wide(){
            System.out.println("寬接口");
        }
        
        public Narrow getConcrete(){
            return (Narrow)new ConcreteCLass();
        }
        
    }
}

接下來(lái)看下黑箱的備忘錄模式,有了上面的介紹,那我們把備忘錄的具體實(shí)現(xiàn)作為內(nèi)部類放到發(fā)起人對(duì)象里面

下面看下具體代碼

//發(fā)起人 加強(qiáng)版 黑箱
public class Originator2 {

    private String state;
    
    public Originator2() {
        
    }
    
    public MementoIF createMemento(){
        return new Memento2(this.state);
    }
    
    //內(nèi)部類 備忘錄
    protected class Memento2 implements MementoIF{
        private String saveState;
        public Memento2(String saveState) {
            this.saveState=saveState;
        }
        public String getSaveState() {
            return saveState;
        }
        public void setSaveState(String saveState) {
            this.saveState = saveState;
        }
    }
    
    
    //從備忘錄對(duì)象里面恢復(fù)狀態(tài)
    public void restoreMemento(MementoIF m){
        this.state=((Memento2)m).getSaveState();
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        System.out.println("當(dāng)前狀態(tài):"+state);
        this.state = state;
    }
}

負(fù)責(zé)人針對(duì)接口編程,代碼如下

//負(fù)責(zé)人
public class Caretaker2 {

    private MementoIF memento;
    
    public MementoIF retrieveMemento(){
        return this.memento;
    }
    
    public void saveMemento(MementoIF m){
        this.memento=m;
    }
    
}

MementoIF這個(gè)接口是窄接口,里面可以有公共使用的方法,這里假設(shè)沒(méi)有方法。
最后測(cè)試下這個(gè)代碼

public class Client2 {

    public static void main(String[] args) {
        Originator2 o=new Originator2();
        Caretaker2 c=new Caretaker2();
        
        //發(fā)起人狀態(tài)改變
        o.setState("Start");
        //負(fù)責(zé)人保存這個(gè)備忘錄
        c.saveMemento(o.createMemento());
        //改變狀態(tài)
        o.setState("End");
        //回到初始
        o.restoreMemento(c.retrieveMemento());

    }

}

/**
最后結(jié)論和之前的一樣,但在設(shè)計(jì)上面已經(jīng)很不同

*/

有時(shí)候發(fā)起人內(nèi)部信息需要保存在別的地方,但是讀取還是發(fā)起人自己,此時(shí)備忘錄模式就可以把發(fā)起人信息對(duì)外封閉起來(lái)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容