<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    無為

    無為則可為,無為則至深!

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      190 Posts :: 291 Stories :: 258 Comments :: 0 Trackbacks
    一、引子

      俗話說:世上難買后悔藥。所以凡事講究個(gè)“三思而后行”,但總常見有人做“痛心疾首”狀:當(dāng)初我要是……。如果真的有《大話西游》中能時(shí)光倒流的“月光寶盒”,那這世上也許會(huì)少一些傷感與后悔——當(dāng)然這只能是癡人說夢(mèng)了。

      但是在我們手指下的程序世界里,卻有的后悔藥買。今天我們要講的備忘錄模式便是程序世界里的“月光寶盒”。

      二、定義與結(jié)構(gòu)

      備忘錄(Memento)模式又稱標(biāo)記(Token)模式。GOF給備忘錄模式的定義為:在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài)。這樣以后就可將該對(duì)象恢復(fù)到原先保存的狀態(tài)。

      在講命令模式的時(shí)候,我們?cè)?jīng)提到利用中間的命令角色可以實(shí)現(xiàn)undo、redo的功能。從定義可以看出備忘錄模式是專門來存放對(duì)象歷史狀態(tài)的,這對(duì)于很好的實(shí)現(xiàn)undo、redo功能有很大的幫助。所以在命令模式中undo、redo功能可以配合備忘錄模式來實(shí)現(xiàn)。

      其實(shí)單就實(shí)現(xiàn)保存一個(gè)對(duì)象在某一時(shí)刻的狀態(tài)的功能,還是很簡(jiǎn)單的——將對(duì)象中要保存的屬性放到一個(gè)專門管理備份的對(duì)象中,需要的時(shí)候則調(diào)用約定好的方法將備份的屬性放回到原來的對(duì)象中去。但是你要好好看看為了能讓你的備份對(duì)象訪問到原對(duì)象中的屬性,是否意味著你就要全部公開或者包內(nèi)公開對(duì)象原本私有的屬性呢?如果你的做法已經(jīng)破壞了封裝,那么就要考慮重構(gòu)一下了。

      備忘錄模式只是GOF對(duì)“恢復(fù)對(duì)象某時(shí)的原有狀態(tài)”這一問題提出的通用方案。因此在如何保持封裝性上——由于受到語言特性等因素的影響,備忘錄模式并沒有詳細(xì)描述,只是基于C++闡述了思路。那么基于Java的應(yīng)用應(yīng)該怎樣來保持封裝呢?我們將在實(shí)現(xiàn)一節(jié)里面討論。

      來看下“月光寶盒”備忘錄模式的組成部分:

      1) 備忘錄(Memento)角色:備忘錄角色存儲(chǔ)“備忘發(fā)起角色”的內(nèi)部狀態(tài)。“備忘發(fā)起角色”根據(jù)需要決定備忘錄角色存儲(chǔ)“備忘發(fā)起角色”的哪些內(nèi)部狀態(tài)。為了防止“備忘發(fā)起角色”以外的其他對(duì)象訪問備忘錄。備忘錄實(shí)際上有兩個(gè)接口,“備忘錄管理者角色”只能看到備忘錄提供的窄接口——對(duì)于備忘錄角色中存放的屬性是不可見的。“備忘發(fā)起角色”則能夠看到一個(gè)寬接口——能夠得到自己放入備忘錄角色中屬性。

      2) 備忘發(fā)起(Originator)角色:“備忘發(fā)起角色”創(chuàng)建一個(gè)備忘錄,用以記錄當(dāng)前時(shí)刻它的內(nèi)部狀態(tài)。在需要時(shí)使用備忘錄恢復(fù)內(nèi)部狀態(tài)。

      3) 備忘錄管理者(Caretaker)角色:負(fù)責(zé)保存好備忘錄。不能對(duì)備忘錄的內(nèi)容進(jìn)行操作或檢查。

      備忘錄模式的類圖真是再簡(jiǎn)單不過了:


      三、舉例

      按照定義中的要求,備忘錄角色要保持完整的封裝。最好的情況便是:備忘錄角色只應(yīng)該暴露操作內(nèi)部存儲(chǔ)屬性的的接口給“備忘發(fā)起角色”。而對(duì)于其他角色則是不可見的。GOF在書中以C++為例進(jìn)行了探討。但是在Java中沒有提供類似于C++中友元的概念。在Java中怎樣才能保持備忘錄角色的封裝呢?

      下面對(duì)三種在Java中可保存封裝的方法進(jìn)行探討。

      第一種就是采用兩個(gè)不同的接口類來限制訪問權(quán)限。這兩個(gè)接口類中,一個(gè)提供比較完備的操作狀態(tài)的方法,我們稱它為寬接口;而另一個(gè)則可以只是一個(gè)標(biāo)示,我們稱它為窄接口。備忘錄角色要實(shí)現(xiàn)這兩個(gè)接口類。這樣對(duì)于“備忘發(fā)起角色”采用寬接口進(jìn)行訪問,而對(duì)于其他的角色或者對(duì)象則采用窄接口進(jìn)行訪問。

      這種實(shí)現(xiàn)比較簡(jiǎn)單,但是需要人為的進(jìn)行規(guī)范約束——而這往往是沒有力度的。

      第二種方法便很好的解決了第一種的缺陷:采用內(nèi)部類來控制訪問權(quán)限。將備忘錄角色作為“備忘發(fā)起角色”的一個(gè)私有內(nèi)部類。好處我不詳細(xì)解釋了,看看代碼吧就明白了。下面的代碼是一個(gè)完整的備忘錄模式的教學(xué)程序。它便采用了第二種方法來實(shí)現(xiàn)備忘錄模式。

      還有一點(diǎn)值得指出的是,在下面的代碼中,對(duì)于客戶程序來說“備忘錄管理者角色”是不可見的,這樣簡(jiǎn)化了客戶程序使用備忘錄模式的難度。下面采用“備忘發(fā)起角色”來調(diào)用訪問“備忘錄管理者角色”,也可以參考門面模式在客戶程序與備忘錄角色之間添加一個(gè)門面角色。

     class Originator{

      //這個(gè)是要保存的狀態(tài)
      private int state= 90;
      //保持一個(gè)“備忘錄管理者角色”的對(duì)象
      private Caretaker c = new Caretaker();
      //讀取備忘錄角色以恢復(fù)以前的狀態(tài)
      public void setMemento(){
       Memento memento = (Memento)c.getMemento();
       state = memento.getState();
       System.out.println("the state is "+state+" now");
      }
      //創(chuàng)建一個(gè)備忘錄角色,并將當(dāng)前狀態(tài)屬性存入,托給“備忘錄管理者角色”存放。

      public void createMemento(){
       c.saveMemento(new Memento(state));
      }
      //this is other business methods...
      //they maybe modify the attribute state

      public void modifyState4Test(int m){
       state = m;
       System.out.println("the state is "+state+" now");
      }

      //作為私有內(nèi)部類的備忘錄角色,它實(shí)現(xiàn)了窄接口,可以看到在第二種方法中寬接口已經(jīng)不再需要
      //注意:里面的屬性和方法都是私有的

      private class Memento implements MementoIF{
       private int state ;
       private Memento(int state){
        this.state = state ;
       }

       private int getState(){
        return state;
       }
      }
     }

     //測(cè)試代碼——客戶程序

     public class TestInnerClass{
      public static void main(String[] args){
       Originator o = new Originator();
       o.createMemento();
       o.modifyState4Test(80);
       o.setMemento();
      }
     }

     //窄接口

     interface MementoIF{}

     //“備忘錄管理者角色”

     class Caretaker{
      private MementoIF m ;
      public void saveMemento(MementoIF m){
       this.m = m;
      }
      public MementoIF getMemento(){
       return m;
      }
     }

      第三種方式是不太推薦使用的:使用clone方法來簡(jiǎn)化備忘錄模式。由于Java提供了clone機(jī)制,這使得復(fù)制一個(gè)對(duì)象變得輕松起來。使用了clone機(jī)制的備忘錄模式,備忘錄角色基本可以省略了,而且可以很好的保持對(duì)象的封裝。但是在為你的類實(shí)現(xiàn)clone方法時(shí)要慎重啊。

      在上面的教學(xué)代碼中,我們簡(jiǎn)單的模擬了備忘錄模式的整個(gè)流程。在實(shí)際應(yīng)用中,我們往往需要保存大量“備忘發(fā)起角色”的歷史狀態(tài)。這時(shí)就要對(duì)我們的“備忘錄管理者角色”進(jìn)行改造,最簡(jiǎn)單的方式就是采用容器來按照順序存放備忘錄角色。這樣就可以很好的實(shí)現(xiàn)undo、redo功能了。

      四、適用情況

      從上面的討論可以看出,使用了備忘錄模式來實(shí)現(xiàn)保存對(duì)象的歷史狀態(tài)可以有效地保持封裝邊界。使用備忘錄可以避免暴露一些只應(yīng)由“備忘發(fā)起角色”管理卻又必須存儲(chǔ)在“備忘發(fā)起角色”之外的信息。把“備忘發(fā)起角色”內(nèi)部信息對(duì)其他對(duì)象屏蔽起來, 從而保持了封裝邊界。

      但是如果備份的“備忘發(fā)起角色”存在大量的信息或者創(chuàng)建、恢復(fù)操作非常頻繁,則可能造成很大的開銷。

      GOF在《設(shè)計(jì)模式》中總結(jié)了使用備忘錄模式的前提:

      1) 必須保存一個(gè)對(duì)象在某一個(gè)時(shí)刻的(部分)狀態(tài), 這樣以后需要時(shí)它才能恢復(fù)到先前的狀態(tài)。

      2) 如果一個(gè)用接口來讓其它對(duì)象直接得到這些狀態(tài),將會(huì)暴露對(duì)象的實(shí)現(xiàn)細(xì)節(jié)并破壞對(duì)象的封裝性。

      五、總結(jié)

      介紹了怎樣來使用備忘錄模式實(shí)現(xiàn)存儲(chǔ)對(duì)象歷史狀態(tài)的功能,并對(duì)基于Java的實(shí)現(xiàn)進(jìn)行了討論。歡迎大家指正。


    凡是有該標(biāo)志的文章,都是該blog博主Caoer(草兒)原創(chuàng),凡是索引、收藏
    、轉(zhuǎn)載請(qǐng)注明來處和原文作者。非常感謝。

    posted on 2006-02-16 09:08 草兒 閱讀(319) 評(píng)論(0)  編輯  收藏 所屬分類: 設(shè)計(jì)模式
    主站蜘蛛池模板: 国产A∨免费精品视频| 亚洲av无码成人影院一区| 无码国产亚洲日韩国精品视频一区二区三区 | 女人18特级一级毛片免费视频| 国产亚洲精品观看91在线| 久久99毛片免费观看不卡| 二个人看的www免费视频| 亚洲午夜一区二区电影院| 亚洲国产另类久久久精品小说| 免费观看国产小粉嫩喷水| 日韩精品免费一区二区三区| 在线观看的免费网站| 免费精品国产自产拍在| 在线观看AV片永久免费| 成人免费无码大片A毛片抽搐色欲| 最近2022中文字幕免费视频| 9277手机在线视频观看免费| 久久免费精品视频| 在线观看片免费人成视频播放 | 好先生在线观看免费播放| 在线视频免费观看爽爽爽| 免费看成人AA片无码视频羞羞网| 91黑丝国产线观看免费| 成人毛片免费视频| 国产男女猛烈无遮挡免费网站 | 亚洲高清无在码在线无弹窗 | 国产成人亚洲精品电影| 看成年女人免费午夜视频| 黄色网页在线免费观看| 久久免费区一区二区三波多野| 亚洲人成片在线观看| 亚洲国产成人久久精品软件| 曰批全过程免费视频观看免费软件| 久香草视频在线观看免费| 日本免费高清视频| 在线免费观看一区二区三区| 亚洲精品无码永久在线观看| 久久精品国产亚洲AV高清热| 亚洲五月综合网色九月色| 亚洲精品国产国语| 久久久久久国产a免费观看不卡|