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

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                第22章 觀察者模式 22.1 韓非子身邊的臥底是誰派來的 《孫子兵法》有云:“知彼知己,百戰不殆;不知彼而知己,一勝一負;不知彼,不知己,每戰必殆”,那怎么才能知己知彼呢?知己是很容易的,自己的軍隊嘛,很容易知根知底,那怎么知彼呢?安插間諜是個好辦法,這是古今中外屢試不爽的方法,我們今天就來講一個間諜的故事。 韓非子大家都應該記得吧,法家的代表人物,主張建立法制社會,實施重罰制度,真是非常有遠見呀!看看現在社會在呼吁什么,建立法制化的社會,這在2000多年前就已經提出了。大家可能還不知道,法家還有一個非常重要的代表人物——李斯。李斯是秦國的丞相,最終被殘忍車裂的那位,李斯和韓非子都是荀子的學生,李斯是師兄,韓非子是師弟,若干年后,李斯成為最強諸侯國秦國的上尉,致力于統一全國,于是安插了間諜到各個國家的重要人物的身邊,以獲取必要的信息,韓非子作為韓國的重量級人物,身邊自然有不少間諜,韓非子做的事,李斯都了如指掌,那可是相隔千里!怎么做到的呢?間諜呀!我們先通過程序把這個過程展現一下,看看李斯是怎么監控韓非子的,先看兩個主角的類圖,如圖22-1所示。 ![](https://box.kancloud.cn/2016-08-14_57b00367e5ec9.jpg) 圖22-1 監控者和被監控者 僅有這兩個對象還是不夠的,我們要解決的是李斯是怎么監控韓非子的?創建一個后臺線程一直處于運行狀態,一旦發現韓非子在吃飯或者娛樂就觸發事件?這是真實世界的翻版,安排了一個間諜,觀察韓非子的生活起居,并上報給李斯,然后李斯再觸發update事件,類圖繼續擴充,如圖22-2所示。 ![](https://box.kancloud.cn/2016-08-14_57b003680595f.jpg) 圖22-2 通過后臺線程監控 這個類圖應該是程序員最容易想到的,你要監控,我就給你找個間諜角色(Spy類),我們來看程序的實現,先看我們的主角韓非子的接口(類似于韓非子這樣的人,被觀察者角色),如代碼清單22-1所示。 代碼清單22-1 被觀察者接口 public?interface?IHanFeiZi?{ ?????//韓非子也是人,也要吃早飯的 ?????public?void?haveBreakfast(); ?????//韓非之也是人,是人就要娛樂活動 ?????public?void?haveFun(); } 對接口進行擴充,增加了兩個狀態isHavingBreakfast(是否在吃早飯)和isHavingFun(是否在娛樂),以方便Spy進行監控,如代碼清單22-2所示。 代碼清單22-2 具體的被觀察者 public?class?HanFeiZi?implements?IHanFeiZi{ ?????//韓非子是否在吃飯,作為監控的判斷標準 ?????private?boolean?isHavingBreakfast?=?false; ?????//韓非子是否在娛樂 ?????private?boolean?isHavingFun?=?false; ?????//韓非子要吃飯了 ?????public?void?haveBreakfast(){ ?????????????System.out.println("韓非子:開始吃飯了..."); ?????????????this.isHavingBreakfast?=true; ?????} ?????//韓非子開始娛樂了 ?????public?void?haveFun(){ ?????????????System.out.println("韓非子:開始娛樂了..."); ?????????????this.isHavingFun?=?true; ?????} ?????//以下是bean的基本方法,getter/setter,不多說 ?????public?boolean?isHavingBreakfast()?{ ?????????????return?isHavingBreakfast; ?????} ?????public?void?setHavingBreakfast(boolean?isHavingBreakfast)?{ ?????????????this.isHavingBreakfast?=?isHavingBreakfast; ?????} ?????public?boolean?isHavingFun()?{ ?????????????return?isHavingFun; ?????} ?????public?void?setHavingFun(boolean?isHavingFun)?{ ?????????????this.isHavingFun?=?isHavingFun; ?????} } 其中有兩個getter/setter方法,這個就沒有在類圖中表示出來,比較簡單,通過isHavingBreakfast和isHavingFun這兩個布爾型變量來判斷韓非子是否在吃飯或者娛樂,韓非子屬于被觀察者,那還有觀察者李斯,我們來看李斯的接口,如代碼清單22-3所示。 代碼清單22-3 抽象觀察者 public?interface?ILiSi?{ ?????//一發現別人有動靜,自己也要行動起來 ?????public?void?update(String?context); } 李斯這類人比較簡單,一發現自己觀察的對象發生了變化,比如吃飯、娛樂,自己立刻也要行動起來,怎么行動呢?如代碼清單22-4所示。 代碼清單22-4 韓非子 public?class?LiSi?implements?ILiSi{ ?????//首先李斯是個觀察者,一旦韓非子有活動,他就知道,他就要向老板匯報 ?????public?void?update(String?str){ ?????????????System.out.println("李斯:觀察到韓非子活動,開始向老板匯報了..."); ?????????????this.reportToQinShiHuang(str); ?????????????System.out.println("李斯:匯報完畢...\n"); ?????} ?????//匯報給秦始皇 ?????private?void?reportToQinShiHuang(String?reportContext){ ?????????????System.out.println("李斯:報告,秦老板!韓非子有活動了--->"+reportContext); ?????} } 兩個重量級的人物都定義出來了,間諜這個“卑鄙”小人是不是也要登臺了,如代碼清單22-5所示。 代碼清單22-5 間諜 class?Spy?extends?Thread{ ?????private?HanFeiZi?hanFeiZi; ?????private?LiSi?liSi; ?????private?String?type; ?????//通過構造函數傳遞參數,我要監控的是誰,誰來監控,要監控什么 ?????public?Spy(HanFeiZi?_hanFeiZi,LiSi?_liSi,String?_type){ ?????????????this.hanFeiZi?=_hanFeiZi; ?????????????this.liSi?=?_liSi; ?????????????this.type?=?_type; ?????} ?????@Override ?????public?void?run(){ ?????????????while(true){ ??????????????????if(this.type.equals("breakfast")){?//監控是否在吃早餐 ??????????????????????????//如果發現韓非子在吃飯,就通知李斯 ??????????????????????????if(this.hanFeiZi.isHavingBreakfast()){ ???????????????????????????????this.liSi.update("韓非子在吃飯"); ???????????????????????????????//重置狀態,繼續監控 ???????????????????????????????this.hanFeiZi.setHavingBreakfast(false); ??????????????????????????}??? ?????????????????????}else{//監控是否在娛樂 ??????????????????????????if(this.hanFeiZi.isHavingFun()){ ???????????????????????????????this.liSi.update("韓非子在娛樂"); ???????????????????????????????this.hanFeiZi.setHavingFun(false); ??????????????????????????} ?????????????????????} ?????????????} ?????} } 監控程序繼承了java.lang.Thread類,可以同時啟動多個線程進行監控,Java的多線程機制還是比較簡單的,繼承Thread類,重寫run()方法,然后new SubThread(),再然后subThread.start()就可以啟動一個線程了。我們建立一個場景類來回顧一下這段歷史,如代碼清單22-6所示。 代碼清單22-6 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?throws?InterruptedException?{ ?????????????//定義出韓非子和李斯 ?????????????LiSi?liSi?=?new?LiSi(); ?????????????HanFeiZi?hanFeiZi?=?new?HanFeiZi(); ?????????????//觀察早餐 ?????????????Watch?watchBreakfast?=?new?Watch(hanFeiZi,liSi,"breakfast"); ?????????????//開始啟動線程,監控 ?????????????watchBreakfast.start(); ?????????????//觀察娛樂情況 ?????????????Watch?watchFun?=?new?Watch(hanFeiZi,liSi,"fun"); ?????????????watchFun.start(); ?????????????//然后我們看看韓非子在干什么 ?????????????Thread.sleep(1000);?//主線程等待1秒后后再往下執行 ?????????????hanFeiZi.haveBreakfast(); ?????????????//韓非子娛樂了 ?????????????Thread.sleep(1000); ?????????????hanFeiZi.haveFun(); ?????} } 運行結果如下所示: 韓非子:開始吃飯了... 李斯:觀察到韓非子活動,開始向老板匯報了... 李斯:報告,秦老板!韓非子有活動了--->韓非子在吃飯 李斯:匯報完畢 韓非子:開始娛樂了... 李斯:觀察到韓非子活動,開始向老板匯報了... 李斯:報告,秦老板!韓非子有活動了--->韓非子在娛樂 李斯:匯報完畢 結果出來,韓非子一吃早飯李斯就知道,韓非子一娛樂李斯也知道,非常正確!結果正確但并不表示你有成績,我告訴你:你的成績是0,甚至是負的!你有沒有看到你的CPU飆升,Eclipse不響應狀態?看到了?看到了你還不想為什么?!看看上面的程序,別的就不多說了,使用了一個死循環while(true)來做監聽,要是用到項目中,你要多少硬件投入進來?你還讓不讓別人的程序運行了?!一臺服務器就跑你這一個程序就完事! 錯誤也看到了,我們必須要修改,這個沒法應用到項目中,而且這個程序根本就不是面向對象的程序,這完全是面向過程的,不改不行,怎么修改呢?我們來想,既然韓非子一吃飯李斯就知道了,那我們為什么不把李斯這個類聚集到韓非子那個類上呢?說改就改,立馬動手,我們來看修改后的類圖,如圖22-3所示。 類圖非常簡單,就是在HanFeiZi類中引用了LiSi實例,看我們程序代碼怎么修改,IHanFeiZi接口完全沒有修改,可以參考代碼清單22-1所示。我們來看實現類的修改,如代碼清單22-7所示。 代碼清單22-7 通過聚集方式的被觀察者 public?class?HanFeiZi?implements?IHanFeiZi{ ?????//把李斯聲明出來 ?????private?ILiSi?liSi?=new?LiSi(); ?????//韓非子要吃飯了 ?????public?void?haveBreakfast(){ ?????????????System.out.println("韓非子:開始吃飯了..."); ?????????????//通知李斯 ?????????????this.liSi.update("韓非子在吃飯"); ?????} ?????//韓非子開始娛樂了 ?????public?void?haveFun(){ ?????????????System.out.println("韓非子:開始娛樂了..."); ?????????????this.liSi.update("韓非子在娛樂"); ?????} } ![](https://box.kancloud.cn/2016-08-14_57b003681c74c.jpg) 圖22-3 通過聚集方式監控 韓非子HanFeiZi實現類就把接口的兩個方法實現就可以了,在每個方法中調用LiSi.update()方法,完成李斯觀察韓非子的職責,李斯的接口和實現類都沒有任何改變,請參考代碼清單22-3、22-4。我們再來看看Client程序的變更,如代碼清單22-8所示。 代碼清單22-8 通過聚集方式的場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//定義出韓非子 ?????????????HanFeiZi?hanFeiZi?=?new?HanFeiZi(); ?????????????//然后我們看看韓非子在干什么 ?????????????hanFeiZi.haveBreakfast(); ?????????????//韓非子娛樂了 ?????????????hanFeiZi.haveFun(); ?????} } 李斯就不用在場景類中定義了,非常簡單,運行結果相同,不再贅述。 我們思考一下,修改后的程序運行結果正確,效率也比較高,是不是應該樂呵樂呵了?大功告成了?稍等等,你想在戰國爭雄的時候,韓非子這么有名望、有實力的人,就只有秦國關心他嗎?想想也不可能呀,確實有一大幫的各國類似于李斯這樣的人在看著他,監視著他的一舉一動,但是看看我們的程序,你在HanFeiZi這個類中定義: private ILiSi liSi =new LiSi(); 這樣一來只有李斯才能觀察到韓非子,這是不對的,也就是說韓非子的活動只通知了李斯一個人,這不可能;再者說了,李斯只觀察韓非子的吃飯、娛樂嗎?政治傾向不關心嗎?思維傾向不關心嗎?殺人放火不關心嗎?也就說韓非子的一系列活動都要通知李斯,這可怎么辦?要按照上面的例子,我們如何修改?這和開閉原則嚴重違背呀,我們的程序有問題,修改如圖22-4所示。 ![](https://box.kancloud.cn/2016-08-14_57b0036831cf6.jpg) 圖22-4 改進后的觀察者和被觀察者 我們把原有類圖做了兩個修改: ● 增加Observable 實現該接口的都是被觀察者,那韓非子是被觀察者,他當然也要實現該接口了,同時他還有與其他庸人相異的事要做,因此他還是要實現IHanFeizi接口。 ● 修改ILiSI接口名稱為Observer 接口名稱修改了一下,這樣顯得更抽象化,所有實現該接口的都是觀察者(類似李斯這樣的)。 Observable是被觀察者,就是類似韓非子這樣的人,在Observable接口中有三個比較重要的方法,分別是addObserver增加觀察者,deleteObserver刪除觀察者,notifyObservers通知所有的觀察者,這是什么意思呢?我這里有一個信息,一個對象,我可以允許有多個對象來察看,你觀察也成,我觀察也成,只要是觀察者就成,也就是說我的改變或動作執行,會通知其他的對象,看程序會更明白一點,先看Observable接口,如代碼清單22-9所示。 代碼清單22-9 被觀察者接口 public?interface?Observable?{ ?????//增加一個觀察者 ?????public?void?addObserver(Observer?observer); ?????//刪除一個觀察者 ?????public?void?deleteObserver(Observer?observer); ?????//既然要觀察,我發生改變了他也應該有所動作,通知觀察者 ?????public?void?notifyObservers(String?context); } 這是一個通用的被觀察者接口,所有的被觀察者都可以實現這個接口。再來看韓非子的實現類,如代碼清單22-10所示。 代碼清單22-10 被觀察者實現類 public?class?HanFeiZi?implements?Observable?,IHanFeiZi{ ?????//定義個變長數組,存放所有的觀察者 ?????private?ArrayList<Observer>?observerList?=?new?ArrayList<Observer>(); ?????//增加觀察者 ?????public?void?addObserver(Observer?observer){ ?????????????this.observerList.add(observer); ?????} ?????//刪除觀察者 ?????public?void?deleteObserver(Observer?observer){ ?????????????this.observerList.remove(observer); ?????} ?????//通知所有的觀察者 ?????public?void?notifyObservers(String?context){ ?????????????for(Observer?observer:observerList){ ?????????????????????observer.update(context); ?????????????} ?????} ?????//韓非子要吃飯了 ?????public?void?haveBreakfast(){ ?????????????System.out.println("韓非子:開始吃飯了..."); ?????????????//通知所有的觀察者 ?????????????this.notifyObservers("韓非子在吃飯"); ?????} ?????//韓非子開始娛樂了 ?????public?void?haveFun(){ ?????????????System.out.println("韓非子:開始娛樂了..."); ?????????????this.notifyObservers("韓非子在娛樂"); ?????} } 觀察者只是把原有的ILiSi接口修改了一個名字而已,如代碼清單22-11所示。 代碼清單22-11 觀察者接口 public?interface?Observer?{ ?????//一發現別人有動靜,自己也要行動起來 ?????public?void?update(String?context); } 然后是三個很無恥的觀察者,咱先看看真實的李斯,如代碼清單22-12所示。 代碼清單22-12 具體的觀察者 public?class?LiSi?implements?Observer{ ?????//首先李斯是個觀察者,一旦韓非子有活動,他就知道,他就要向老板匯報 ?????public?void?update(String?str){ ?????????????System.out.println("李斯:觀察到韓非子活動,開始向老板匯報了..."); ?????????????this.reportToQinShiHuang(str); ?????????????System.out.println("李斯:匯報完畢...\n"); ?????} ?????//匯報給秦始皇 ?????private?void?reportToQinShiHuang(String?reportContext){ ?????????????System.out.println("李斯:報告,秦老板!韓非子有活動了-->"+reportContext); ?????} } 李斯是真有其人,以下兩個觀察者王斯和劉斯是杜撰出來的,如代碼清單22-13所示。 代碼清單22-13 杜撰的觀察者 public?class?WangSi?implements?Observer{ ?????//王斯,看到韓非子有活動 ?????public?void?update(String?str){ ?????????????System.out.println("王斯:觀察到韓非子活動,自己也開始活動了..."); ?????????????this.cry(str); ?????????????System.out.println("王斯:哭死了...\n"); ?????} ?????//一看韓非子有活動,他就痛哭 ?????private?void?cry(String?context){ ?????????????System.out.println("王斯:因為"+context+",--所以我悲傷呀!"); ?????} } public?class?LiuSi?implements?Observer{????? ?????//劉斯,觀察到韓非子活動后,自己也得做一些事 ?????public?void?update(String?str){ ?????????????System.out.println("劉斯:觀察到韓非子活動,開始動作了..."); ?????????????this.happy(str); ?????????????System.out.println("劉斯:樂死了\n"); ?????} ?????//一看韓非子有變化,他就快樂 ?????private?void?happy(String?context){ ?????????????System.out.println("劉斯:因為"?+context+",--所以我快樂呀!"?); ?????} } 所有的歷史人物都在場了,那我們來看看這場歷史鬧劇是如何演繹的,如代碼清單22-14所示。 代碼清單22-14 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//三個觀察者產生出來 ?????????????Observer?liSi?=?new?LiSi(); ?????????????Observer?wangSi?=?new?WangSi(); ?????????????Observer?liuSi?=?new?LiuSi();????? ?????????????//定義出韓非子 ?????????????HanFeiZi?hanFeiZi?=?new?HanFeiZi(); ?????????????//我們后人根據歷史,描述這個場景,有三個人在觀察韓非子 ?????????????hanFeiZi.addObserver(liSi); ?????????????hanFeiZi.addObserver(wangSi); ?????????????hanFeiZi.addObserver(liuSi);????? ?????????????//然后這里我們看看韓非子在干什么 ?????????????hanFeiZi.haveBreakfast(); ?????} } 運行結果如下所示: 韓非子:開始吃飯了... 李斯:觀察到韓非子活動,開始向老板匯報了... 李斯:報告,秦老板!韓非子有活動了--->韓非子在吃飯 李斯:匯報完畢... 王斯:觀察到韓非子活動,自己也開始活動了... 王斯:因為韓非子在吃飯——所以我悲傷呀! 王斯:哭死了... 劉斯:觀察到韓非子活動,開始動作了... 劉斯:因為韓非子在吃飯——所以我快樂呀! 劉斯:樂死了 好了,結果也正確了,也符合開閉原則了,同時也實現類間解耦,想再加觀察者?繼續實現Observer接口就成了,這時候必須修改Client程序,因為你的業務都發生了變化。這就是觀察者模式。
                  <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>

                              哎呀哎呀视频在线观看