1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        監(jiān)聽(tīng)器模式和觀察者模式的關(guān)系,寫點(diǎn)你不知道的

        共 5271字,需瀏覽 11分鐘

         ·

        2021-06-16 12:25

        大家好,我是躍哥,好久不見(jiàn)。躍哥之前干過(guò)一系列的設(shè)計(jì)模式文章,感覺(jué)收獲很大,不知道小伙伴們有沒(méi)有認(rèn)真看過(guò)呢?今天,我們來(lái)點(diǎn)續(xù)集,聊聊監(jiān)聽(tīng)者和觀察者模式中,你不知道的事。

        前言

        無(wú)論大家在實(shí)踐中是否自己實(shí)現(xiàn)過(guò)觀察者模式或監(jiān)聽(tīng)器模式,但肯定間接使用過(guò)。比如Spring的事件機(jī)制,大多數(shù)人肯定都用過(guò),只是沒(méi)留意而已。

        今天這篇文章主要圍繞觀察者模式、監(jiān)聽(tīng)器模式,以及它們之間的關(guān)系展開。不僅用實(shí)例介紹它們的使用,而且也會(huì)聊一聊Spring事件機(jī)制對(duì)觀察者模式的實(shí)踐。

        監(jiān)聽(tīng)器模式和觀察者模式怎么看起來(lái)是一樣的?

        先聊聊設(shè)計(jì)模式

        為什么要使用監(jiān)聽(tīng)模式,直接調(diào)用不好嗎?這我們就要說(shuō)說(shuō)設(shè)計(jì)模式的好處了。設(shè)計(jì)模式(Design pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。使用設(shè)計(jì)模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。同時(shí),采用設(shè)計(jì)模式之后,代碼能夠達(dá)到低耦合、低依賴的效果。

        也就是說(shuō),即便不用設(shè)計(jì)模式,直接硬編碼也能實(shí)現(xiàn)。但如果考慮到代碼的耦合性、依賴性、擴(kuò)展性等問(wèn)題,設(shè)計(jì)模式便是更好的選擇。比如觀察者模式就能達(dá)到解耦、異步等效果。

        觀察者模式的定義

        觀察者模式定義了對(duì)象之間的一對(duì)多依賴,即一個(gè)主題對(duì)應(yīng)多個(gè)觀察者。當(dāng)一個(gè)主題對(duì)象改變狀態(tài)時(shí),它的所有依賴者(觀察者)都會(huì)收到通知并自動(dòng)更新。比如用戶訂閱某個(gè)訂閱號(hào)或公眾號(hào),當(dāng)發(fā)新消息時(shí),會(huì)發(fā)送給所有訂閱者。


        觀察者模式解決的是對(duì)象和對(duì)象之間的依賴關(guān)系。當(dāng)多個(gè)對(duì)象依賴一個(gè)對(duì)象的關(guān)系時(shí),一個(gè)主題對(duì)象狀態(tài)改變,需要通知所有觀察者對(duì)象。

        監(jiān)聽(tīng)器模式并不是一個(gè)新的設(shè)計(jì)模式,它是觀察者模式在特定場(chǎng)景下的一種改造和應(yīng)用。通常,觀察者模式的主題在通知觀察者時(shí),通知中不包含任何信息。如果這個(gè)過(guò)程中攜帶了一些其他信息,那么主題本身就成為了事件源,而攜帶信息的封裝類就成為了事件。此時(shí)的觀察者模式,也就升級(jí)為監(jiān)聽(tīng)器了。監(jiān)聽(tīng)器模式是觀察者模式的另一種形態(tài)。

        觀察者模式實(shí)例

        先來(lái)看看觀察者模式的代碼實(shí)現(xiàn),可以直接使用JDK自帶的Observer,也可以自定義對(duì)應(yīng)的API。單從JDK自帶觀察者模式的API,也可以看出該設(shè)計(jì)模式的分量(雖然在Java被廢棄了)。

        我們這里采用自定義相關(guān)類,主要包括主題和觀察者兩種對(duì)象。

        首先定義一個(gè)主題對(duì)應(yīng)的接口Subject:

        public interface Subject {

        /**
        * 注冊(cè)定義
        */

        void registerObserver(Observer observer);

        /**
        * 發(fā)送通知
        */

        void notifyObservers(Object msg);

        }

        主題接口中定義了兩個(gè)方法一個(gè)用來(lái)注冊(cè)觀察者,一個(gè)用來(lái)發(fā)送通知。定義這個(gè)主題的具體實(shí)現(xiàn)類ConcreteSubject:

        public class ConcreteSubject implements Subject {

        /**
        * 觀察者集合
        */

        private List<Observer> observers = new ArrayList<>();

        @Override
        public void registerObserver(Observer observer) {
        // 添加訂閱關(guān)系
        observers.add(observer);
        }

        @Override
        public void notifyObservers(Object msg) {
        // 通知訂閱者
        for (Observer observer : observers) {
        observer.update(msg);
        }
        }
        }

        實(shí)現(xiàn)類中存儲(chǔ)了,觀察者的集合,這樣就實(shí)現(xiàn)了主題和觀察者之間一對(duì)多的關(guān)系。

        創(chuàng)建一個(gè)觀察者接口Observer,方便管理:

        public interface Observer {
        // 處理業(yè)務(wù)邏輯
        void update(Object msg);
        }

        定義觀察者接口的具體實(shí)現(xiàn)類ConcreteObserver:

        public class ConcreteObserver implements Observer {

        @Override
        public void update(Object msg) {
        // 業(yè)務(wù)邏輯實(shí)現(xiàn)
        System.out.println("ConcreteObserver 接收到主題的消息: " + msg);
        }
        }

        在實(shí)現(xiàn)類中,打印一行消息。當(dāng)然,在實(shí)踐的過(guò)程中,這個(gè)實(shí)現(xiàn)類可以通過(guò)匿名類的形式創(chuàng)建,這樣就具體的匿名類就在registerObserver時(shí)定義了。

        下面來(lái)測(cè)試一下:

        public class ObserverTest {

        public static void main(String[] args) {
        Subject subject = new ConcreteSubject();
        Observer observer = new ConcreteObserver();
        // 注冊(cè)觀察者
        subject.registerObserver(observer);
        // 發(fā)布消息
        subject.notifyObservers("來(lái)自Subject的消息");
        }
        }

        執(zhí)行程序,打印結(jié)果:

        ConcreteObserver 接收到主題的消息: 來(lái)自Subject的消息

        說(shuō)明可以正常接收到主題發(fā)布的消息。

        在上面的實(shí)現(xiàn)中,可以看出已經(jīng)達(dá)到了解耦合的效果,同時(shí)減少了依賴關(guān)系。每個(gè)觀察者根本不需要知道發(fā)布者處理了什么業(yè)務(wù)邏輯,也不依賴于發(fā)布者的業(yè)務(wù)模型,只關(guān)心自己的邏輯處理即可。

        監(jiān)聽(tīng)模式實(shí)例

        監(jiān)聽(tīng)器模式通常包含三個(gè)角色:事件源、事件對(duì)象、事件監(jiān)聽(tīng)器。如果觀察者模式中的類名和方法對(duì)照改一下,并不改變業(yè)務(wù)邏輯,我們來(lái)看看是啥效果。

        比如,將Observer改名為L(zhǎng)istener,將其update方法改為onClick();將Subject的實(shí)現(xiàn)類ConcreteSubject改名為L(zhǎng)istenerSupport,將registerObserver方法更名為addListener……

        定義一個(gè)事件對(duì)象Event,用來(lái)傳遞事件信息:

        public class Event {
        private String data;
        private String type;

        Event(String data, String type) {
        this.data = data;
        this.type = type;
        }
        // 省略getter/setter
        }

        原來(lái)主題的訂閱對(duì)象Observer改名為L(zhǎng)istener之后,成為監(jiān)聽(tīng)器:

        public interface Listener {
        void onClick(Event event);
        }

        為監(jiān)聽(tīng)器提供一個(gè)實(shí)現(xiàn)類,當(dāng)然在實(shí)踐中也可以采用匿名類創(chuàng)建的方式:

        public class ListenerA implements Listener {

        @Override
        public void onClick(Event event) {
        System.out.println("觸發(fā)事件,type:" + event.getType() + ",data:" + event.getData());
        }
        }

        原來(lái)的主題ConcreteSubject對(duì)照ListenerSupport成為事件管理器:

        public class ListenerSupport {

        private List<Listener> listeners = new ArrayList<>();

        public void addListener(Listener listener) {
        listeners.add(listener);
        }

        public void triggerEvent(Event event) {
        for (Listener listener : listeners) {
        listener.onClick(event);
        }
        }
        }

        對(duì)應(yīng)的測(cè)試代碼:

        public class EventTest {

        public static void main(String[] args) {
        Listener listener = new ListenerA();
        ListenerSupport listenerSupport = new ListenerSupport();
        listenerSupport.addListener(listener);
        listenerSupport.triggerEvent(new Event("dataA", "typeA"));
        }
        }

        執(zhí)行程序,打印信息如下:

        觸發(fā)事件,type:typeAdata:dataA

        通過(guò)上面的對(duì)照代碼,我們可以看出,即便業(yè)務(wù)邏輯不變,經(jīng)過(guò)重命名的觀察者模式已經(jīng)變?yōu)楸O(jiān)聽(tīng)器模式了。而它們的對(duì)照關(guān)系是:事件源對(duì)照ConcreteSubject(主題)、事件對(duì)象對(duì)照update方法的Object、事件監(jiān)聽(tīng)器對(duì)照ConcreteObserver(訂閱者)。這也再次證明了所說(shuō)的“監(jiān)聽(tīng)器模式是觀察者模式的另一種形態(tài)”。

        觀察者模式和監(jiān)聽(tīng)器模式對(duì)比

        用一張圖,來(lái)比較觀察者模式和監(jiān)聽(tīng)器模式的聯(lián)系和區(qū)別: 

        通過(guò)對(duì)比可以發(fā)監(jiān)聽(tīng)器模式的優(yōu)勢(shì)是:在很多場(chǎng)景中,通知中附帶了一些必不可少的其他信息,而事件Event可以對(duì)這些信息進(jìn)行封裝,使它本身?yè)碛辛硕鄳B(tài)的特性。每個(gè)事件對(duì)象就可以包含不同的信息。從這個(gè)層面來(lái)說(shuō),事件監(jiān)聽(tīng)器模式是對(duì)觀察者模式進(jìn)行了進(jìn)一步的抽象。

        Spring中的最佳實(shí)踐

        觀察者模式的經(jīng)典應(yīng)用算是Spring事件驅(qū)動(dòng)模型了,它便是基于觀察者模式實(shí)現(xiàn)的,同時(shí)也是項(xiàng)目中最常見(jiàn)的事件監(jiān)聽(tīng)器。

        Spring中觀察者模式包含四個(gè)角色:事件、事件監(jiān)聽(tīng)器、事件源、事件管理。

        事件:ApplicationEvent是所有事件對(duì)象的父類。ApplicationEvent繼承自jdk的EventObject,所有的事件都需要繼承ApplicationEvent,并且通過(guò)source得到事件源。Spring 提供了很多內(nèi)置事件,比如:ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent、RequestHandledEvent。

        事件監(jiān)聽(tīng)器:ApplicationListener,也就是觀察者,繼承自jdk的EventListener,該類中只有一個(gè)方法onApplicationEvent,當(dāng)監(jiān)聽(tīng)的事件發(fā)生后該方法會(huì)被執(zhí)行。

        事件源:ApplicationContext,ApplicationContext是Spring的核心容器,在事件監(jiān)聽(tīng)中ApplicationContext可以作為事件的發(fā)布者,也就是事件源。因?yàn)锳pplicationContext繼承自ApplicationEventPublisher。在ApplicationEventPublisher中定義了事件發(fā)布的方法:publishEvent(Object event)。

        事件管理:ApplicationEventMulticaster,用于事件監(jiān)聽(tīng)器的注冊(cè)和事件的廣播。監(jiān)聽(tīng)器的注冊(cè)就是通過(guò)它來(lái)實(shí)現(xiàn)的,它的作用是把Applicationcontext發(fā)布的Event廣播給它的監(jiān)聽(tīng)器列表。

        小結(jié)

        通過(guò)本篇文章我們知道,監(jiān)聽(tīng)器模式的本質(zhì)就是觀察者模式,先將回調(diào)函數(shù)注冊(cè)到被觀察對(duì)象,當(dāng)被觀察對(duì)象發(fā)生變化時(shí),通過(guò)回調(diào)函數(shù)告知觀察者/監(jiān)聽(tīng)者。而Spring中事件管理也是基于觀察者模式實(shí)現(xiàn)的,算是一個(gè)比較經(jīng)典的案例。






        0、重磅!兩萬(wàn)字長(zhǎng)文總結(jié),梳理 Java 入門進(jìn)階哪些事(推薦收藏)

        瀏覽 108
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            麻豆免费在线视频 | 综合五月 | 国产丰满岳乱妇在线观看 | 超碰人人操人人爱 | 天堂成人影院 | 亚洲免费成人网站 | 日日夜夜天天综合久久 | 操操操操操操网 | 囯产精品久久欠久久久久久九秃大 | 亚洲中文无 |