<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                24.4 備忘錄模式的擴展 24.4.1 clone方式的備忘錄 大家還記得在第13章中講的原型模式嗎?我們可以通過復制的方式產生一個對象的內部狀態,這是一個很好的辦法,發起人角色只要實現Cloneable就成,比較簡單,我們來看類圖,如圖24-5所示。 ![](https://box.kancloud.cn/2016-08-14_57b003699ca21.jpg) 圖24-5 Clone方式的備忘錄 從類圖上看,發起人角色融合了發起人角色和備忘錄角色,具有雙重功效,如代碼清單24-12所示。 代碼清單24-12 融合備忘錄的發起人角色 public?class?Originator?implements?Cloneable{ ?????//內部狀態 ?????private?String?state?=?""; ????? ?????public?String?getState()?{ ?????????????return?state; ?????} ?????public?void?setState(String?state)?{ ?????????????this.state?=?state; ?????} ?????//創建一個備忘錄 ?????public?Originator?createMemento(){ ?????????????return?this.clone(); ?????} ?????//恢復一個備忘錄 ?????public?void?restoreMemento(Originator?_originator){ ?????????????this.setState(_originator.getState()); ?????} ?????//克隆當前對象 ?????@Override ?????protected?Originator?clone(){ ?????????????try?{ ?????????????????????return?(Originator)super.clone(); ?????????????}?catch?(CloneNotSupportedException?e)?{ ?????????????????????e.printStackTrace(); ?????????????} ?????????????return?null; ?????} } 增加了clone方法,產生了一個備份對象,需要使用的時候再還原,我們再來看管理員角色,如代碼清單24-13所示。 代碼清單24-13 備忘錄管理員角色 public?class?Caretaker?{ ?????//發起人對象 ?????private?Originator?originator; ?????public?Originator?getOriginator()?{ ?????????????return?originator; ?????} ?????public?void?setOriginator(Originator?originator)?{ ?????????????this.originator?=?originator; ?????} } 沒什么太大變化,只是備忘錄角色轉換成了發起人角色,還是一個簡單的JavaBean。我們來想想這種模式是不是還可以簡化?要管理員角色干什么?就是為了管理備忘錄角色,現在連備忘錄角色都被合并了,還留著它干嗎?我們想辦法把它也精簡掉,如代碼清單24-14所示。 代碼清單24-14 發起人自主備份和恢復 public?class?Originator?implements?Cloneable{ ?????private?Originator?backup; ?????//內部狀態 ?????private?String?state?=?""; ?????public?String?getState()?{ ?????????????return?state; ?????} ?????public?void?setState(String?state)?{ ?????????????this.state?=?state; ?????} ?????//創建一個備忘錄 ?????public?void?createMemento(){ ?????????????this.backup?=?this.clone(); ?????} ?????//恢復一個備忘錄 ?????public?void?restoreMemento(){ ?????????????//在進行恢復前應該進行斷言,防止空指針 ?????????????this.setState(this.backup.getState()); ?????} ?????//克隆當前對象 ?????@Override ?????protected?Originator?clone(){ ?????????????try?{ ?????????????????????return?(Originator)super.clone(); ?????????????}?catch?(CloneNotSupportedException?e)?{ ?????????????????????e.printStackTrace(); ?????????????} ?????????????return?null; ?????} } 可能你要發問了,這和備忘錄模式的定義不相符,它定義是“在該對象之外保存這個狀態”,而你卻把這個狀態保存在了發起人內部。是的,設計模式定義的誕生比Java的出世略早,它沒有想到Java程序是這么有活力,有遠見,而且在面向對象的設計中,即使把一個類封裝在另一個類中也是可以做到的,何況一個小小的對象復制,這是它的設計模式完全沒有預見到的,我們把它彌補回來。 再來看看Client是如何調用的,如代碼清單24-15所示。 代碼清單24-15 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//定義發起人 ?????????????Originator?originator?=?new?Originator(); ?????????????//建立初始狀態 ?????????????originator.setState("初始狀態..."); ?????????????System.out.println("初始狀態是:"+originator.getState()); ?????????????//建立備份 ?????????????originator.createMemento(); ?????????????//修改狀態 ?????????????originator.setState("修改后的狀態..."); ?????????????System.out.println("修改后狀態是:"+originator.getState()); ?????????????//恢復原有狀態 ?????????????originator.restoreMemento(); ?????????????System.out.println("恢復后狀態是:"+originator.getState()); ?????} } 運行結果如下所示: 初始狀態是:初始狀態... 修改后狀態是:修改后的狀態... 恢復后狀態是:初始狀態... 運行結果是我們所希望的,程序精簡了很多,而且高層模塊的依賴也減少了,這正是我們期望的效果。現在我們來考慮一下原型模式深拷貝和淺拷貝的問題,在復雜的場景下它會讓你的程序邏輯異常混亂,出現錯誤也很難跟蹤。因此Clone方式的備忘錄模式適用于較簡單的場景。 注意 使用Clone方式的備忘錄模式,可以使用在比較簡單的場景或者比較單一的場景中,盡量不要與其他的對象產生嚴重的耦合關系。 24.4.2 多狀態的備忘錄模式 讀者應該看到我們以上講解都是單狀態的情況,在實際的開發中一個對象不可能只有一個狀態,一個JavaBean有多個屬性非常常見,這都是它的狀態,如果照搬我們以上講解的備忘錄模式,是不是就要寫一堆的狀態備份、還原語句?這不是一個好辦法,這種類似的非智力勞動越多,犯錯誤的幾率越大,那我們有什么辦法來處理多個狀態的備份問題呢? 下面我們來講解一個對象全狀態備份方案,它有多種處理方式,比如使用Clone的方式就可以解決,使用數據技術也可以解決(DTO回寫到臨時表中)等,我們要講的方案就對備忘錄模式繼續擴展一下,實現一個JavaBean對象的所有狀態的備份和還原,如圖24-6所示。 ![](https://box.kancloud.cn/2016-08-14_57b00369b8ac9.jpg) 圖24-6 多狀態的備忘錄模式 還是比較簡單的類圖,增加了一個BeanUtils類,其中backupProp是把發起人的所有屬性值轉換到HashMap中,方便備忘錄角色存儲;restoreProp方法則是把HashMap中的值返回到發起人角色中。可能各位要說了,為什么要使用HashMap,直接使用Originator對象的拷貝不是一個很好的方法嗎?可以這樣做,你就破壞了發起人的通用性,你在做恢復動作的時候需要對該對象進行多次賦值操作,也容易產生錯誤。我們先來看發起人角色,如代碼清單24-16所示。 代碼清單24-16 發起人角色 public?class?Originator?{ ?????//內部狀態 ?????private?String?state1?=?""; ?????private?String?state2?=?""; ?????private?String?state3?=?"";? ?????public?String?getState1()?{ ?????????????return?state1; ?????} ?????public?void?setState1(String?state1)?{ ?????????????this.state1?=?state1; ?????} ?????public?String?getState2()?{ ?????????????return?state2; ?????} ?????public?void?setState2(String?state2)?{ ?????????????this.state2?=?state2; ?????} ?????public?String?getState3()?{ ?????????????return?state3; ?????} ?????public?void?setState3(String?state3)?{ ?????????????this.state3?=?state3; ?????} ?????//創建一個備忘錄 ?????public?Memento?createMemento(){ ?????????????return?new?Memento(BeanUtils.backupProp(this)); ?????} ?????//恢復一個備忘錄 ?????public?void?restoreMemento(Memento?_memento){ ?????????????BeanUtils.restoreProp(this,?_memento.getStateMap()); ?????} ?????//增加一個toString方法 ?????@Override ?????public?String?toString(){ ?????????????return?"state1="?+state1+"\nstat2="+state2+"\nstate3="+state3;? ?????} } 覆寫toString方法是為了方便打印,可以讓展示的結果更清晰。我們再來看BeanUtils工具類,如代碼清單24-17所示。 代碼清單24-17 BeanUtils工具類 public?class?BeanUtils?{ ?????//把bean的所有屬性及數值放入到Hashmap中 ?????public?static?HashMap<String,Object>?backupProp(Object?bean){ ?????????????HashMap<String,Object>?result?=?new?HashMap<String,Object>(); ?????????????try?{ ?????????????????????//獲得Bean描述 ?????????????????????BeanInfo?beanInfo=Introspector.getBeanInfo(bean.getClass()); ?????????????????????//獲得屬性描述 ?????????????????????PropertyDescriptor[]?descriptors=beanInfo.getPropertyDescriptors(); ?????????????????????//遍歷所有屬性 ?????????????????????for(PropertyDescriptor?des:descriptors){ ?????????????????????????????//屬性名稱 ?????????????????????????????String?fieldName?=?des.getName(); ?????????????????????????????//讀取屬性的方法 ?????????????????????????????Method?getter?=?des.getReadMethod(); ?????????????????????????????//讀取屬性值 ?????????????????????????????Object?fieldValue=getter.invoke(bean,new?Object[]{}); ????????????????????if(!fieldName.equalsIgnoreCase("class")){ ?????????????????????????????result.put(fieldName,?fieldValue); ????????????????????} ???????????????} ??????????}?catch?(Exception?e)?{ ???????????????//異常處理 ??????????} ??????????return?result; ?????} ?????//把HashMap的值返回到bean中 ?????public?static?void?restoreProp(Object?bean,HashMap<String,Object>?propMap){ ??????????try?{ ???????????????//獲得Bean描述 ???????????????BeanInfo?beanInfo?=?Introspector.getBeanInfo(bean.getClass()); ???????????????//獲得屬性描述 ???????????????PropertyDescriptor[]?descriptors?=?beanInfo.getPropertyDescriptors(); ???????????????//遍歷所有屬性 ???????????????for(PropertyDescriptor?des:descriptors){ ????????????????????//屬性名稱 ????????????????????String?fieldName?=?des.getName(); ????????????????????//如果有這個屬性 ????????????????????if(propMap.containsKey(fieldName)){ ?????????????????????????//寫屬性的方法 ?????????????????????????Method?setter?=?des.getWriteMethod(); ?????????????????????????setter.invoke(bean,?new?Object[]{propMap.get(fieldName)}); ????????????????????} ???????????????} ??????????}?catch?(Exception?e)?{ ???????????????//異常處理 ???????????????System.out.println("shit"); ???????????????e.printStackTrace(); ??????????} ?????} } 該類大家在項目中會經常用到,可以作為參考使用。類似的功能有很多工具已經提供,比如Spring、Apache工具集commons等,大家也可以直接使用。我們再來看備忘錄角色,如代碼清單24-18所示。 代碼清單24-18 備忘錄角色 public?class?Memento?{ ?????//接受HashMap作為狀態 ?????private?HashMap<String,Object>?stateMap; ?????//接受一個對象,建立一個備份 ?????public?Memento(HashMap<String,Object>?map){ ?????????????this.stateMap?=?map; ?????} ?????public?HashMap<String,Object>?getStateMap()?{ ?????????????return?stateMap; ?????} ?????public?void?setStateMap(HashMap<String,Object>?stateMap)?{ ?????????????this.stateMap?=?stateMap; ?????} } 我們再編寫一個場景類,看看我們的成果是否正確,如代碼清單24-19所示。 代碼清單24-19 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//定義出發起人 ?????????????Originator?ori?=?new?Originator(); ?????????????//定義出備忘錄管理員 ?????????????Caretaker?caretaker?=?new?Caretaker(); ?????????????//初始化 ?????????????ori.setState1("中國"); ?????????????ori.setState2("強盛"); ?????????????ori.setState3("繁榮"); ?????????????System.out.println("===初始化狀態===\n"+ori); ?????????????//創建一個備忘錄 ?????????????caretaker.setMemento(ori.createMemento()); ?????????????//修改狀態值 ?????????????ori.setState1("軟件"); ?????????????ori.setState2("架構"); ?????????????ori.setState3("優秀"); ?????????????System.out.println("\n===修改后狀態===\n"+ori); ?????????????//恢復一個備忘錄 ?????????????ori.restoreMemento(caretaker.getMemento()); ?????????????System.out.println("\n===恢復后狀態===\n"+ori); ?????} } 運行結果如下所示: ===初始化狀態=== state1=中國 stat2=強盛 state3=繁榮 ===修改后狀態=== state1=軟件 stat2=架構 state3=優秀 ===恢復后狀態=== state1=中國 stat2=強盛 state3=繁榮 通過這種方式的改造,不管有多少狀態都沒有問題,直接把原有的對象所有屬性都備份了一遍,想恢復當時的點數據?那太容易了! 注意 如果要設計一個在運行期決定備份狀態的框架,則建議采用AOP框架來實現,避免采用動態代理無謂地增加程序邏輯復雜性。 24.4.3 多備份的備忘錄 不知道你有沒有做過系統級別的維護?比如Backup Administrator(備份管理員),每天負責查看系統的備份情況,所有的備份都是由自動化腳本產生的。有一天,突然有一個重要的系統說我數據庫有點問題,請把上一個月末的數據拉出來恢復,那怎么辦?對備份管理員來說,這很好辦,直接根據時間戳找到這個備份,還原回去就成了,但是對于我們剛剛學習的備忘錄模式卻行不通,為什么呢?它對于一個確定的發起人,永遠只有一份備份,在這種情況下,單一的備份就不能滿足要求了,我們需要設計一套多備份的架構。 我們先來說一個名詞,檢查點(Check Point),也就是你在備份的時候做的戳記,系統級的備份一般是時間戳,那我們程序的檢查點該怎么設計呢?一般是一個有意義的字符串。 我們只要把通用代碼中的Caretaker管理員稍做修改就可以了,如代碼清單24-20所示。 代碼清單24-20 備忘錄管理員 public?class?Caretaker?{ ?????//容納備忘錄的容器 ?????private?HashMap<String,Memento>?memMap?=?new?HashMap<String,Memento>(); ?????public?Memento?getMemento(String?idx)?{ ?????????????return?memMap.get(idx); ?????} ?????public?void?setMemento(String?idx,Memento?memento)?{ ?????????????this.memMap.put(idx,?memento); ?????} } 把容納備忘錄的容器修改為Map類型就可以了,場景類也稍做改動,如代碼清單24-21所示。 代碼清單24-21 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//定義出發起人 ?????????????Originator?originator?=?new?Originator(); ?????????????//定義出備忘錄管理員 ?????????????Caretaker?caretaker?=?new?Caretaker(); ?????????????//創建兩個備忘錄 ?????????????caretaker.setMemento("001",originator.createMemento()); ?????????????caretaker.setMemento("002",originator.createMemento()); ?????????????//恢復一個指定標記的備忘錄 ?????????????originator.restoreMemento(caretaker.getMemento("001")); ?????} } 注意 內存溢出問題,該備份一旦產生就裝入內存,沒有任何銷毀的意向,這是非常危險的。因此,在系統設計時,要嚴格限定備忘錄的創建,建議增加Map的上限,否則系統很容易產生內存溢出情況。 24.4.4 封裝得更好一點 在系統管理上,一個備份的數據是完全、絕對不能修改的,它保證數據的潔凈,避免數據污染而使備份失去意義。在我們的設計領域中,也存在著同樣的問題,備份是不能被篡改的,也就是說需要縮小備份出的備忘錄的閱讀權限,保證只能是發起人可讀就成了,那怎么才能做到這一點呢?使用內置類,如圖24-7所示。 ![](https://box.kancloud.cn/2016-08-14_57b00369d05f6.jpg) 圖24-7 使用內置類的備忘錄模式 這也是比較簡單的,建立一個空接口IMemento——什么方法屬性都沒有的接口,然后在發起人Originator類中建立一個內置類(也叫做類中類)Memento實現IMemento接口,同時也實現自己的業務邏輯,如代碼清單24-22所示。 代碼清單24-22 發起人角色 public?class?Originator?{ ?????//內部狀態 ?????private?String?state?=?""; ?????public?String?getState()?{ ?????????????return?state; ?????} ?????public?void?setState(String?state)?{ ?????????????this.state?=?state; ?????} ?????//創建一個備忘錄 ?????public?IMemento?createMemento(){ ?????????????return?new?Memento(this.state); ?????} ?????//恢復一個備忘錄 ?????public?void?restoreMemento(IMemento?_memento){ ?????????????this.setState(((Memento)_memento).getState()); ?????} ?????//內置類 ?????private?class?Memento?implements?IMemento{ ?????????????//發起人的內部狀態 ?????????????private?String?state?=?""; ?????????????//構造函數傳遞參數 ?????????????private?Memento(String?_state){ ?????????????????????this.state?=?_state; ?????????????} ?????????????private?String?getState()?{ ?????????????????????return?state; ?????????????} ?????????????private?void?setState(String?state)?{ ?????????????????????this.state?=?state; ?????????????} ?????} } 內置類Memento全部是private的訪問權限,也就是說除了發起人外,別人休想訪問到,那如果要產生關聯關系又應如何處理呢?通過接口!別忘記了我們還有一個空接口是公共的訪問權限,如代碼清單24-23所示。 代碼清單24-23 備忘錄的空接口 public?interface?IMemento?{ } 我們再來看管理者,如代碼清單24-24所示。 代碼清單24-24 備忘錄管理者 public?class?Caretaker?{ ?????//備忘錄對象 ?????private?IMemento?memento; ?????public?IMemento?getMemento()?{ ?????????????return?memento; ?????} ?????public?void?setMemento(IMemento?memento)?{ ?????????????this.memento?=?memento; ?????} } 全部通過接口訪問,這當然沒有問題,如果你想訪問它的屬性那是肯定不行的。但是安全是相對的,沒有絕對的安全,可以使用refelect反射修改Memento的數據。 在這里我們使用了一個新的設計方法:雙接口設計,我們的一個類可以實現多個接口,在系統設計時,如果考慮對象的安全問題,則可以提供兩個接口,一個是業務的正常接口,實現必要的業務邏輯,叫做寬接口;另外一個接口是一個空接口,什么方法都沒有,其目的是提供給子系統外的模塊訪問,比如容器對象,這個叫做窄接口,由于窄接口中沒有提供任何操縱數據的方法,因此相對來說比較安全。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看