1. 「補(bǔ)課」進(jìn)行時(shí):設(shè)計(jì)模式(15)——觀察者模式

        共 7973字,需瀏覽 16分鐘

         ·

        2020-11-30 10:15

        65922c7e4b8aaf7e82e6e8a9a87df3ee.webp

        1. 前文匯總

        「補(bǔ)課」進(jìn)行時(shí):設(shè)計(jì)模式系列

        2. 觀察者模式

        2.1 定義

        觀察者模式(Observer Pattern)也叫做發(fā)布訂閱模式(Publish/subscribe),它是一個(gè)在項(xiàng)目中經(jīng)常使用的模式,其定義如下:

        Define a one-to-many dependency between objects so that when oneobject changes state,all its dependents are notified and updatedautomatically.(定義對象間一種一對多的依賴關(guān)系,使得每當(dāng)一個(gè)對象改變狀態(tài),則所有依賴于它的對象都會得到通知并被自動更新。)

        2.2 通用類圖

        e55355eac2dd02a5d2264944efc6d1f9.webp
        • Subject 被觀察者: 定義被觀察者必須實(shí)現(xiàn)的職責(zé),它必須能夠動態(tài)地增加、取消觀察者。它一般是抽象類或者是實(shí)現(xiàn)類,僅僅完成作為被觀察者必須實(shí)現(xiàn)的職責(zé):管理觀察者并通知觀察者。
        • ConcreteSubject 具體的被觀察者: 定義被觀察者自己的業(yè)務(wù)邏輯,同時(shí)定義對哪些事件進(jìn)行通知。
        • Observer 觀察者: 觀察者接收到消息后,即進(jìn)行update(更新方法)操作,對接收到的信息進(jìn)行處理。
        • ConcreteObserver 具體的觀察者: 每個(gè)觀察在接收到消息后的處理反應(yīng)是不同,各個(gè)觀察者有自己的處理邏輯。

        2.3 通用代碼

        Subject 被觀察者:

        public?abstract?class?Subject?{
        ????//?定義一個(gè)觀察者數(shù)組
        ????private?Vector?obsVector?=?new?Vector<>();
        ????//?添加一個(gè)觀察者
        ????public?void?addObserver(Observer?obsVector)?{
        ????????this.obsVector.add(obsVector);
        ????}
        ????//?刪除一個(gè)觀察者
        ????public?void?delObserver(Observer?observer)?{
        ????????this.obsVector.remove(observer);
        ????}
        ????//?通知所有觀察者
        ????public?void?notifyObservers()?{
        ????????for?(Observer?obs?:?this.obsVector)?{
        ????????????obs.update();
        ????????}
        ????}
        }

        ConcreteSubject 具體的被觀察者:

        public?class?ConcreteSubject?extends?Subject?{
        ????public?void?doSomething()?{
        ????????//?具體的業(yè)務(wù)
        ????????super.notifyObservers();
        ????}
        }

        Observer 觀察者:

        public?interface?Observer?{
        ????void?update();
        }

        ConcreteObserver 具體的觀察者:

        public?class?ConcreteObserver?implements?Observer{
        ????@Override
        ????public?void?update()?{
        ????????System.out.println("進(jìn)行消息處理");
        ????}
        }

        測試場景類:

        public?class?Test?{
        ????public?static?void?main(String[]?args)?{
        ????????//?創(chuàng)建一個(gè)被觀察者
        ????????ConcreteSubject?subject?=?new?ConcreteSubject();
        ????????//?創(chuàng)建一個(gè)觀察者
        ????????Observer?observer?=?new?ConcreteObserver();
        ????????//?觀察者觀察被觀察者
        ????????subject.addObserver(observer);
        ????????//?觀察者開始活動了
        ????????subject.doSomething();
        ????}
        }

        3. 一個(gè)案例

        觀察者模式是設(shè)計(jì)模式中的超級模式,有關(guān)他的應(yīng)用隨處可見。

        就比如說微信公眾號,我每天推送一篇博文內(nèi)容,訂閱的用戶都能夠在我發(fā)布推送之后及時(shí)接收到推送,方便地在手機(jī)端進(jìn)行閱讀。

        訂閱者接口(觀察者)

        public?interface?Subscriber?{
        ????void?receive(String?publisher,?String?articleName);
        }

        微信客戶端(具體觀察者)

        public?class?WeChatClient?implements?Subscriber?{

        ????private?String?username;

        ????public?WeChatClient(String?username)?{
        ????????this.username?=?username;
        ????}

        ????@Override
        ????public?void?receive(String?publisher,?String?articleName)?{
        ????????System.out.println(String.format("用戶<%s>?接收到?<%s>微信公眾號?的推送,文章標(biāo)題為?<%s>",?username,?publisher,?articleName));
        ????}
        }

        一個(gè)微信客戶端(具體觀察者)

        public?class?Publisher?{
        ????private?List?subscribers;
        ????private?boolean?pubStatus?=?false;

        ????public?Publisher()?{
        ????????subscribers?=?new?ArrayList<>();
        ????}

        ????protected?void?subscribe(Subscriber?subscriber)?{
        ????????this.subscribers.add(subscriber);
        ????}

        ????protected?void?unsubscribe(Subscriber?subscriber)?{
        ????????if?(this.subscribers.contains(subscriber))?{
        ????????????this.subscribers.remove(subscriber);
        ????????}
        ????}

        ????protected?void?notifySubscribers(String?publisher,?String?articleName)?{
        ????????if?(this.pubStatus?==?false)?{
        ????????????return;
        ????????}
        ????????for?(Subscriber?subscriber?:?this.subscribers)?{
        ????????????subscriber.receive(publisher,?articleName);
        ????????}
        ????????this.clearPubStatus();
        ????}

        ????protected?void?setPubStatus()?{
        ????????this.pubStatus?=?true;
        ????}

        ????protected?void?clearPubStatus()?{
        ????????this.pubStatus?=?false;
        ????}
        }

        具體目標(biāo)

        public?class?WeChatAccounts?extends?Publisher?{
        ????private?String?name;

        ????public?WeChatAccounts(String?name)?{
        ????????this.name?=?name;
        ????}

        ????public?void?publishArticles(String?articleName,?String?content)?{
        ????????System.out.println(String.format("\n<%s>微信公眾號?發(fā)布了一篇推送,文章名稱為?<%s>,內(nèi)容為?<%s>?",?this.name,?articleName,?content));
        ????????setPubStatus();
        ????????notifySubscribers(this.name,?articleName);
        ????}
        }

        測試類

        public?class?Test?{
        ????public?static?void?main(String[]?args)?{
        ????????WeChatAccounts?accounts?=?new?WeChatAccounts("極客挖掘機(jī)");

        ????????WeChatClient?user1?=?new?WeChatClient("張三");
        ????????WeChatClient?user2?=?new?WeChatClient("李四");
        ????????WeChatClient?user3?=?new?WeChatClient("王五");

        ????????accounts.subscribe(user1);
        ????????accounts.subscribe(user2);
        ????????accounts.subscribe(user3);

        ????????accounts.publishArticles("設(shè)計(jì)模式?|?觀察者模式及典型應(yīng)用",?"觀察者模式的內(nèi)容...");

        ????????accounts.unsubscribe(user1);
        ????????accounts.publishArticles("設(shè)計(jì)模式?|?單例模式及典型應(yīng)用",?"單例模式的內(nèi)容....");
        ????}
        }

        測試結(jié)果

        <極客挖掘機(jī)>微信公眾號?發(fā)布了一篇推送,文章名稱為?<設(shè)計(jì)模式?|?觀察者模式及典型應(yīng)用>,內(nèi)容為?<觀察者模式的內(nèi)容...>?
        用戶<張三>?接收到?<極客挖掘機(jī)>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計(jì)模式?|?觀察者模式及典型應(yīng)用>
        用戶<李四>?接收到?<極客挖掘機(jī)>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計(jì)模式?|?觀察者模式及典型應(yīng)用>
        用戶<王五>?接收到?<極客挖掘機(jī)>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計(jì)模式?|?觀察者模式及典型應(yīng)用>

        <極客挖掘機(jī)>微信公眾號?發(fā)布了一篇推送,文章名稱為?<設(shè)計(jì)模式?|?單例模式及典型應(yīng)用>,內(nèi)容為?<單例模式的內(nèi)容....>?
        用戶<李四>?接收到?<極客挖掘機(jī)>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計(jì)模式?|?單例模式及典型應(yīng)用>
        用戶<王五>?接收到?<極客挖掘機(jī)>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計(jì)模式?|?單例模式及典型應(yīng)用>

        4. JDK 對的觀察者模式的支持

        觀察者模式在 Java 語言中的地位非常重要。在 JDK 的 java.util 包中,提供了 Observable 類以及 Observer 接口,它們構(gòu)成了JDK對觀察者模式的支持。

        java.util.Observer 接口中,僅有一個(gè) update(Observable o, Object arg) 方法,當(dāng)觀察目標(biāo)發(fā)生變化時(shí)被調(diào)用:

        public?interface?Observer?{
        ????void?update(Observable?o,?Object?arg);
        }

        java.util.Observable 類則為目標(biāo)類:

        public?class?Observable?{
        ????private?boolean?changed?=?false;
        ????private?Vector?obs;

        ????public?Observable()?{
        ????????obs?=?new?Vector<>();
        ????}
        ????//?用于注冊新的觀察者對象到向量中
        ????public?synchronized?void?addObserver(Observer?o)?{
        ????????if?(o?==?null)
        ????????????throw?new?NullPointerException();
        ????????if?(!obs.contains(o))?{
        ????????????obs.addElement(o);
        ????????}
        ????}
        ????//?用于刪除向量中的某一個(gè)觀察者對象
        ????public?synchronized?void?deleteObserver(Observer?o)?{
        ????????obs.removeElement(o);
        ????}

        ????public?void?notifyObservers()?{
        ????????notifyObservers(null);
        ????}
        ????//?通知方法,用于在方法內(nèi)部循環(huán)調(diào)用向量中每一個(gè)觀察者的update()方法
        ????public?void?notifyObservers(Object?arg)?{
        ????????Object[]?arrLocal;

        ????????synchronized?(this)?{
        ????????????if?(!changed)
        ????????????????return;
        ????????????arrLocal?=?obs.toArray();
        ????????????clearChanged();
        ????????}

        ????????for?(int?i?=?arrLocal.length-1;?i>=0;?i--)
        ????????????((Observer)arrLocal[i]).update(this,?arg);
        ????}
        ????//?用于清空向量,即刪除向量中所有觀察者對象
        ????public?synchronized?void?deleteObservers()?{
        ????????obs.removeAllElements();
        ????}
        ????//?該方法被調(diào)用后會設(shè)置一個(gè)boolean類型的內(nèi)部標(biāo)記變量changed的值為true,表示觀察目標(biāo)對象的狀態(tài)發(fā)生了變化
        ????protected?synchronized?void?setChanged()?{
        ????????changed?=?true;
        ????}
        ????//?用于將changed變量的值設(shè)為false,表示對象狀態(tài)不再發(fā)生改變或者已經(jīng)通知了所有的觀察者對象,調(diào)用了它們的update()方法
        ????protected?synchronized?void?clearChanged()?{
        ????????changed?=?false;
        ????}
        ????//?返回對象狀態(tài)是否改變
        ????public?synchronized?boolean?hasChanged()?{
        ????????return?changed;
        ????}
        ????//?返回向量中觀察者的數(shù)量
        ????public?synchronized?int?countObservers()?{
        ????????return?obs.size();
        ????}
        }

        相比較我們自己的示例 Publisher , java.util.Observer 中多了并發(fā)和 NPE 方面的考慮 。

        使用 JDK 對觀察者模式的支持,改寫一下上面的示例:

        增加一個(gè)通知類 WechatNotice ,用于推送通知的傳遞:

        public?class?WechatNotice?{
        ????private?String?publisher;
        ????private?String?articleName;
        ????//?省略?get/set
        }

        然后改寫 WeChatClientWeChatAccounts ,分別實(shí)現(xiàn) JDK 的 Observer 接口和繼承 Observable 類:

        public?class?WeChatClient?implements?Observer?{

        ????private?String?username;

        ????public?WeChatClient(String?username)?{
        ????????this.username?=?username;
        ????}

        ????@Override
        ????public?void?update(Observable?o,?Object?arg)?{
        ????????WechatNotice?notice?=?(WechatNotice)?arg;
        ????????System.out.println(String.format("用戶<%s>?接收到?<%s>微信公眾號?的推送,文章標(biāo)題為?<%s>",?username,?notice.getPublisher(),?notice.getArticleName()));
        ????}
        }

        public?class?WeChatAccounts?extends?Observable?{
        ????private?String?name;

        ????public?WeChatAccounts(String?name)?{
        ????????this.name?=?name;
        ????}

        ????public?void?publishArticles(String?articleName,?String?content)?{
        ????????System.out.println(String.format("\n<%s>微信公眾號?發(fā)布了一篇推送,文章名稱為?<%s>,內(nèi)容為?<%s>?",?this.name,?articleName,?content));
        ????????setChanged();
        ????????notifyObservers(new?WechatNotice(this.name,?articleName));
        ????}
        }

        最后是一個(gè)測試類:

        public?class?Test?{
        ????public?static?void?main(String[]?args)?{
        ????????WeChatAccounts?accounts?=?new?WeChatAccounts("極客挖掘機(jī)");

        ????????WeChatClient?user1?=?new?WeChatClient("張三");
        ????????WeChatClient?user2?=?new?WeChatClient("李四");
        ????????WeChatClient?user3?=?new?WeChatClient("王五");

        ????????accounts.addObserver(user1);
        ????????accounts.addObserver(user2);
        ????????accounts.addObserver(user3);

        ????????accounts.publishArticles("設(shè)計(jì)模式?|?觀察者模式及典型應(yīng)用",?"觀察者模式的內(nèi)容...");

        ????????accounts.deleteObserver(user1);
        ????????accounts.publishArticles("設(shè)計(jì)模式?|?單例模式及典型應(yīng)用",?"單例模式的內(nèi)容....");
        ????}
        }

        測試結(jié)果:

        <極客挖掘機(jī)>微信公眾號?發(fā)布了一篇推送,文章名稱為?<設(shè)計(jì)模式?|?觀察者模式及典型應(yīng)用>,內(nèi)容為?<觀察者模式的內(nèi)容...>?
        用戶<王五>?接收到?<極客挖掘機(jī)>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計(jì)模式?|?觀察者模式及典型應(yīng)用>
        用戶<李四>?接收到?<極客挖掘機(jī)>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計(jì)模式?|?觀察者模式及典型應(yīng)用>
        用戶<張三>?接收到?<極客挖掘機(jī)>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計(jì)模式?|?觀察者模式及典型應(yīng)用>

        <極客挖掘機(jī)>微信公眾號?發(fā)布了一篇推送,文章名稱為?<設(shè)計(jì)模式?|?單例模式及典型應(yīng)用>,內(nèi)容為?<單例模式的內(nèi)容....>?
        用戶<王五>?接收到?<極客挖掘機(jī)>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計(jì)模式?|?單例模式及典型應(yīng)用>
        用戶<李四>?接收到?<極客挖掘機(jī)>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計(jì)模式?|?單例模式及典型應(yīng)用>

        和前面的示例結(jié)果完全一致。





        感謝閱讀d231f4aaabc4692d4cb29c1a182afdf8.webp



        瀏覽 45
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 牛牛AV| 翔田千里一区二区在线 | 黄色中文字幕 | 黄污视频在线看 | 国产专区在线 |