[设计模式]事件监听器模式

回调函数

先从生活中的例子来理解这种过程:
我点了一份外卖,外卖到了外卖小哥会自动拨打我的电话通知我去拿外卖。
这个过程就是回调。

OK,这是一个simple的过程,那么用代码来实现如何实现。

实现一个简单的回调函数模型

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

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

/**
 * @author jaymin
* 客户,负责点餐和留下联系方式
* 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()); } /** * 联系方式,配送员送到之后通过这个方法通知客户 */ public void callback(){ System.out.println("食物已到达,请下楼取餐"); } }
  • DeliveryPerson
package com.xjm.design.eventlistener.callback;
/**
 * @author jaymin
* 配送员,负责按照客户的要求进行配送,配送完后通知客户进行用餐。
* 2021/1/10 21:17 */ public class DeliveryPerson { public void supply(UserOrder userOrder){ System.out.println("当前用户下单的食品清单:"+userOrder.getFoodName()); System.out.println("到达商家拿到食物"); System.out.println("抵达客户留下的地址,通知客户进行取餐"); 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

这样有什么坏处?硬编码了,不利于扩展.下面我们通过接口来实现多态.

重构回调函数

  • 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
* 配送员,负责按照客户的要求进行配送,配送完后通知客户进行用餐。
* 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("当前用户下单的食品清单:" + foodName); System.out.println("到达商家拿到食物"); System.out.println("抵达客户留下的地址,通知客户进行取餐"); callback.callback(); } }
  • CallbackDemo

这里使用lambda来代替内部类,写法上更加简洁.

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("食物已到达,请下楼取餐"),"冰红茶").execute();
    }
}

调用过程

callback

JDK中的回调函数-Runnable接口

Thread类中内置了一个private Runnable target;,在start的时候会回调Runnable接口的run方法.

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

扩展阅读

java 回调函数解读

事件监听器模式

由一组监听器订阅特定事件的发布,一旦该事件进行了发布,所有的监听器都会做出响应,其中的响应则是上文所述的回调函数.

事件监听器模式组成成员

  • 事件源: Event Source.被监听的对象,一旦事件源发生某个动作时,则调用其内置的事件监听器的方法,将事件对象进行广播.
  • 事件监听器: Event Listener. 监听事件源,可以对事件进行判断,进而响应.
  • 事件对象: Event Object.事件,通常为事件源广播的内容。
eventListener

代码示例

  • Event

定义事件发布的内容

package com.tea.design.eventlistener.pattern;

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

/**
 * @author jaymin
* 事件对象.
* 2021/1/11 22:10 */ @Data @AllArgsConstructor @NoArgsConstructor @Builder public class Event { private String message; }
  • EventListener

定义事件监听器接口,声明处理事件的方法

package com.tea.design.eventlistener.pattern;

/**
 * @author jaymin
* 事件监听器
* 2021/1/11 22:18 */ public interface EventListener { /** * 处理事件 * @param event 事件 */ void processEvent(Event event); }
  • OfflineNewsEventListener

监听事件进行日志打印

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("日报头条:今天大事件:{}",event.getMessage());
    }
}
  • OnlineNewsEventListener

监听事件进行日志打印

package com.tea.design.eventlistener.pattern;

import lombok.extern.slf4j.Slf4j;

/**
 * @author jaymin
* 如果发生大新闻,网络传媒要处理报道.
* 2021/1/11 22:27 */ @Slf4j public class OnlineNewsEventListener implements EventListener { @Override public void processEvent(Event event) { log.info("微博爆料:今天的爆炸新闻:{}", event.getMessage()); } }
  • EventSource

事件源,提供注册监听器方法与发布事件方法

package com.tea.design.eventlistener.pattern;

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

/**
 * @author jaymin
 * 2021/1/11 22:30
 */
public class EventSource {
    /**
     * 将所有的监听者进行存储
     */
    private List listenerList = new ArrayList<>();

    /**
     * 注册监听者
     * @param eventListener
     */
    public void addListener(EventListener eventListener){
        listenerList.add(eventListener);
    }

    /**
     * 发布事件
     * @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

总结

事件监听器模式与观察者模式大同小异,实现的思想是让被监听者/被观察者持有所有的监听器类,当需要是事件发布的时候,对这些监听器进行消息广播。
监听器实现至统一的接口,事件源会将事件对象作为参数进行传输,然后每个监听器处理自己对应的业务.
这体现了面向接口编程的设计原则,让代码耦合度更加松散。


缺点:

  • 多个监听器操作同一事件对象,监听器与监听器无法感知对方对该对象进行了什么操作。
  • 同步调用,如果部分监听器执行时间长,会增加耗时。可以采用异步处理的方式规避.

扩展阅读

  • JDK中的观察者模式
  • 消息队列的broker消费模式
  • Spring容器的事件监听器应用

你可能感兴趣的:([设计模式]事件监听器模式)