<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 5437字,需瀏覽 11分鐘

           ·

          2021-06-08 12:33

          前言

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

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

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

          先聊聊設(shè)計模式

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

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

          觀察者模式的定義

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


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

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

          觀察者模式實例

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

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

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

          public interface Subject {

          /**
          * 注冊定義
          */

          void registerObserver(Observer observer);

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

          void notifyObservers(Object msg);

          }

          主題接口中定義了兩個方法一個用來注冊觀察者,一個用來發(fā)送通知。定義這個主題的具體實現(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);
          }
          }
          }

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

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

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

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

          public class ConcreteObserver implements Observer {

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

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

          下面來測試一下:

          public class ObserverTest {

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

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

          ConcreteObserver 接收到主題的消息: 來自Subject的消息

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

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

          監(jiān)聽模式實例

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

          比如,將Observer改名為Listener,將其update方法改為onClick();將Subject的實現(xiàn)類ConcreteSubject改名為ListenerSupport,將registerObserver方法更名為addListener……

          定義一個事件對象Event,用來傳遞事件信息:

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

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

          原來主題的訂閱對象Observer改名為Listener之后,成為監(jiān)聽器:

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

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

          public class ListenerA implements Listener {

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

          原來的主題ConcreteSubject對照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);
          }
          }
          }

          對應(yīng)的測試代碼:

          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

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

          觀察者模式和監(jiān)聽器模式對比

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

          通過對比可以發(fā)監(jiān)聽器模式的優(yōu)勢是:在很多場景中,通知中附帶了一些必不可少的其他信息,而事件Event可以對這些信息進行封裝,使它本身擁有了多態(tài)的特性。每個事件對象就可以包含不同的信息。從這個層面來說,事件監(jiān)聽器模式是對觀察者模式進行了進一步的抽象。

          Spring中的最佳實踐

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

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

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

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

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

          事件管理:ApplicationEventMulticaster,用于事件監(jiān)聽器的注冊和事件的廣播。監(jiān)聽器的注冊就是通過它來實現(xiàn)的,它的作用是把Applicationcontext發(fā)布的Event廣播給它的監(jiān)聽器列表。

          小結(jié)

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

          往期推薦

          直觀講解一下RPC調(diào)用和HTTP調(diào)用的區(qū)別!

          Github代碼fork之后,如何與原倉庫進行同步?

          @PostConstruct注解是Spring提供的?今天講點不一樣的

          再聊面試,這次關(guān)于錢,關(guān)于培訓(xùn),關(guān)于內(nèi)卷

          Spring的Lifecycle和SmartLifecycle,可以沒用過,但不能不知道!



          如果你覺得這篇文章不錯,那么,下篇通常會更好。添加微信好友,可備注“加群”(微信號:zhuan2quan)

          一篇文章就看透技術(shù)本質(zhì)的人,
            和花一輩子都看不清的人,
            注定是截然不同的搬磚生涯。
          ▲ 按關(guān)注”程序新視界“,洞察技術(shù)內(nèi)幕
          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色视频在线免费观 | 久久久精品理论A级A片 | 一级A片三人撸片 | 成人毛片18女人免费看 | 亚洲成人大香蕉 |