[設(shè)計(jì)模式]事件監(jiān)聽器模式

回調(diào)函數(shù)

先從生活中的例子來理解這種過程:
我點(diǎn)了一份外賣,外賣到了外賣小哥會(huì)自動(dòng)撥打我的電話通知我去拿外賣。
這個(gè)過程就是回調(diào)。

OK,這是一個(gè)simple的過程,那么用代碼來實(shí)現(xiàn)如何實(shí)現(xiàn)。

實(shí)現(xiàn)一個(gè)簡單的回調(diào)函數(shù)模型

  • UserOrder
package com.xjm.design.eventlistener.callback;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author jaymin<br>
 * 客戶,負(fù)責(zé)點(diǎn)餐和留下聯(lián)系方式<br>
 * 2021/1/10 21:16
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserOrder {
    /**
     * 食物名稱
     */
    private String foodName;

    public UserOrder(DeliveryPerson deliveryPerson){
        deliveryPerson.supply(UserOrder.builder().foodName("冰紅茶").build());
    }

    /**
     * 聯(lián)系方式,配送員送到之后通過這個(gè)方法通知客戶
     */
    public void callback(){
        System.out.println("食物已到達(dá),請(qǐng)下樓取餐");
    }
}
  • DeliveryPerson
package com.xjm.design.eventlistener.callback;
/**
 * @author jaymin<br>
 * 配送員,負(fù)責(zé)按照客戶的要求進(jìn)行配送,配送完后通知客戶進(jìn)行用餐。<br>
 * 2021/1/10 21:17
 */
public class DeliveryPerson {

    public void supply(UserOrder userOrder){
        System.out.println("當(dāng)前用戶下單的食品清單:"+userOrder.getFoodName());
        System.out.println("到達(dá)商家拿到食物");
        System.out.println("抵達(dá)客戶留下的地址,通知客戶進(jìn)行取餐");
        userOrder.callback();
    }
}
  • CallbackDemo
package com.xjm.design.eventlistener.callback;

/**
 * @author jaymin
 * 2021/1/10 21:34
 */
public class CallbackDemo {
    public static void main(String[] args) {
        UserOrder userOrder = new UserOrder(new DeliveryPerson());
    }
}
  • Result
Result

這樣有什么壞處?硬編碼了,不利于擴(kuò)展.下面我們通過接口來實(shí)現(xiàn)多態(tài).

重構(gòu)回調(diào)函數(shù)

  • Callback
package com.xjm.design.eventlistener.callback;

/**
 * @author jaymin
 * 2021/1/10 21:41
 */
public interface Callback {

    void callback();

}
  • DeliveryPerson
package com.xjm.design.eventlistener.callback;

/**
 * @author jaymin<br>
 * 配送員,負(fù)責(zé)按照客戶的要求進(jìn)行配送,配送完后通知客戶進(jìn)行用餐。<br>
 * 2021/1/10 21:17
 */

public class DeliveryPerson {
    private String foodName;
    private Callback callback;


    public DeliveryPerson(Callback callback, String foodName) {
        this.callback = callback;
        this.foodName = foodName;
    }

    public void execute() {
        System.out.println("當(dāng)前用戶下單的食品清單:" + foodName);
        System.out.println("到達(dá)商家拿到食物");
        System.out.println("抵達(dá)客戶留下的地址,通知客戶進(jìn)行取餐");
        callback.callback();
    }
}
  • CallbackDemo

這里使用lambda來代替內(nèi)部類,寫法上更加簡潔.

package com.xjm.design.eventlistener.callback;

/**
 * @author jaymin
 * 2021/1/10 21:34
 */
public class CallbackDemo {
    public static void main(String[] args) {
        new DeliveryPerson(()-> System.out.println("食物已到達(dá),請(qǐng)下樓取餐"),"冰紅茶").execute();
    }
}

調(diào)用過程

callback

JDK中的回調(diào)函數(shù)-Runnable接口

Thread類中內(nèi)置了一個(gè)private Runnable target;,在start的時(shí)候會(huì)回調(diào)Runnable接口的run方法.

        new Thread(()-> System.out.println("callback")).start();

擴(kuò)展閱讀

java 回調(diào)函數(shù)解讀

事件監(jiān)聽器模式

由一組監(jiān)聽器訂閱特定事件的發(fā)布,一旦該事件進(jìn)行了發(fā)布,所有的監(jiān)聽器都會(huì)做出響應(yīng),其中的響應(yīng)則是上文所述的回調(diào)函數(shù).

事件監(jiān)聽器模式組成成員

  • 事件源: Event Source.被監(jiān)聽的對(duì)象,一旦事件源發(fā)生某個(gè)動(dòng)作時(shí),則調(diào)用其內(nèi)置的事件監(jiān)聽器的方法,將事件對(duì)象進(jìn)行廣播.
  • 事件監(jiān)聽器: Event Listener. 監(jiān)聽事件源,可以對(duì)事件進(jìn)行判斷,進(jìn)而響應(yīng).
  • 事件對(duì)象: Event Object.事件,通常為事件源廣播的內(nèi)容。
eventListener

代碼示例

  • Event

定義事件發(fā)布的內(nèi)容

package com.tea.design.eventlistener.pattern;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author jaymin<br>
 * 事件對(duì)象.<br>
 * 2021/1/11 22:10
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Event {

    private String message;

}
  • EventListener

定義事件監(jiān)聽器接口,聲明處理事件的方法

package com.tea.design.eventlistener.pattern;

/**
 * @author jaymin<br>
 * 事件監(jiān)聽器<br>
 * 2021/1/11 22:18
 */
public interface EventListener {
    /**
     * 處理事件
     * @param event 事件
     */
    void processEvent(Event event);
}
  • OfflineNewsEventListener

監(jiān)聽事件進(jìn)行日志打印

package com.tea.design.eventlistener.pattern;

import lombok.extern.slf4j.Slf4j;

/**
 * @author jaymin
 * 2021/1/11 22:28
 */
@Slf4j
public class OfflineNewsEventListener implements EventListener{
    @Override
    public void processEvent(Event event) {
        log.info("日?qǐng)?bào)頭條:今天大事件:{}",event.getMessage());
    }
}
  • OnlineNewsEventListener

監(jiān)聽事件進(jìn)行日志打印

package com.tea.design.eventlistener.pattern;

import lombok.extern.slf4j.Slf4j;

/**
 * @author jaymin<br>
 * 如果發(fā)生大新聞,網(wǎng)絡(luò)傳媒要處理報(bào)道.<br>
 * 2021/1/11 22:27
 */
@Slf4j
public class OnlineNewsEventListener implements EventListener {
    @Override
    public void processEvent(Event event) {
        log.info("微博爆料:今天的爆炸新聞:{}", event.getMessage());
    }
}
  • EventSource

事件源,提供注冊(cè)監(jiān)聽器方法與發(fā)布事件方法

package com.tea.design.eventlistener.pattern;

import java.util.ArrayList;
import java.util.List;

/**
 * @author jaymin
 * 2021/1/11 22:30
 */
public class EventSource {
    /**
     * 將所有的監(jiān)聽者進(jìn)行存儲(chǔ)
     */
    private List<EventListener> listenerList = new ArrayList<>();

    /**
     * 注冊(cè)監(jiān)聽者
     * @param eventListener
     */
    public void addListener(EventListener eventListener){
        listenerList.add(eventListener);
    }

    /**
     * 發(fā)布事件
     * @param event
     */
    public void publishEvent(Event event){
        listenerList.forEach(eventListener -> eventListener.processEvent(event));
    }
}
  • EventListenerDemo
package com.tea.design.eventlistener.pattern;

/**
 * @author jaymin
 * 2021/1/11 22:33
 */
public class EventListenerDemo {
    public static void main(String[] args) {
        EventSource eventSource = new EventSource();
        OnlineNewsEventListener onlineNewsEventListener = new OnlineNewsEventListener();
        OfflineNewsEventListener offlineNewsEventListener = new OfflineNewsEventListener();
        eventSource.addListener(onlineNewsEventListener);
        eventSource.addListener(offlineNewsEventListener);
        eventSource.publishEvent(Event.builder().message("特朗普被推特永久封禁!").build());
    }
}
  • Result
result

總結(jié)

事件監(jiān)聽器模式與觀察者模式大同小異,實(shí)現(xiàn)的思想是讓被監(jiān)聽者/被觀察者持有所有的監(jiān)聽器類,當(dāng)需要是事件發(fā)布的時(shí)候,對(duì)這些監(jiān)聽器進(jìn)行消息廣播。
監(jiān)聽器實(shí)現(xiàn)至統(tǒng)一的接口,事件源會(huì)將事件對(duì)象作為參數(shù)進(jìn)行傳輸,然后每個(gè)監(jiān)聽器處理自己對(duì)應(yīng)的業(yè)務(wù).
這體現(xiàn)了面向接口編程的設(shè)計(jì)原則,讓代碼耦合度更加松散。


缺點(diǎn):

  • 多個(gè)監(jiān)聽器操作同一事件對(duì)象,監(jiān)聽器與監(jiān)聽器無法感知對(duì)方對(duì)該對(duì)象進(jìn)行了什么操作。
  • 同步調(diào)用,如果部分監(jiān)聽器執(zhí)行時(shí)間長,會(huì)增加耗時(shí)??梢圆捎卯惒教幚淼姆绞揭?guī)避.

擴(kuò)展閱讀

  • JDK中的觀察者模式
  • 消息隊(duì)列的broker消費(fèi)模式
  • Spring容器的事件監(jiān)聽器應(yīng)用
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 訂閱者模式 訂閱者和發(fā)布者模式,通常用于消息隊(duì)列中。一般有兩種形式來實(shí)現(xiàn)消息隊(duì)列,一是使用生產(chǎn)者和消費(fèi)者來實(shí)現(xiàn),二...
    會(huì)飛的賊er閱讀 2,127評(píng)論 0 0
  • 訂閱者模式 訂閱者和發(fā)布者模式,通常用于消息隊(duì)列中。一般有兩種形式來實(shí)現(xiàn)消息隊(duì)列,一是使用生產(chǎn)者和消費(fèi)者來實(shí)現(xiàn),二...
    _挑燈看劍_閱讀 14,115評(píng)論 1 18
  • 1. 了解事件監(jiān)聽器 事件監(jiān)聽器包括3個(gè)部分,事件、事件源、事件監(jiān)聽器。 事件,主要用于傳遞參數(shù),例如用戶登錄,可...
    曾澤浩閱讀 1,556評(píng)論 0 2
  • 推薦指數(shù): 6.0 書籍主旨關(guān)鍵詞:特權(quán)、焦點(diǎn)、注意力、語言聯(lián)想、情景聯(lián)想 觀點(diǎn): 1.統(tǒng)計(jì)學(xué)現(xiàn)在叫數(shù)據(jù)分析,社會(huì)...
    Jenaral閱讀 5,958評(píng)論 0 5
  • 昨天,在回家的路上,坐在車?yán)镉圃沼圃盏乜粗摹度龉衬墓适隆?,我被里面的?nèi)容深深吸引住了,盡管上學(xué)時(shí)...
    夜闌曉語閱讀 3,922評(píng)論 2 9

友情鏈接更多精彩內(nèi)容