浅谈事件机制是如何优雅编码的

一、背景

       不知道是否有人同博主一样对 “抛事件”、“事件监听” 这些词语很陌生,在初入社会之前甚至没有听过,也没有用过,博主也是在接触游戏开发领域才了解一丝皮毛,其实它是有一个专业统称——事件机制。在介绍它之前呢,博主想提问一个问题:在你编码生涯中,是否会遇到令人抓狂的产品经理经常会需求变动,让你无时无刻都在写代码,改代码,写代码,改代码...

       ......

       有一天,产品经理说:今天需要在用户注册后增加一个短信发送功能,用于发送一些最近的活动,经过日日夜夜的编码,你完成了功能....

       过了两天,产品经理又说了:还需要在用户注册后发送一些新人福利的需求。此时你有些不耐烦了,但你还是完成了需求....

       过了一段时间,产品经理觉得新人都只是在薅羊毛,但不购买,所以要取消发送新人福利这个需求,而是改成购买送积分,通过积分来兑换礼品。此时你已经忍不了,但是想了想生活还得过,就算了。

        又过了一段时间,产品经理又屁颠屁颠跑来和你说:又有新需求变动啦。此时,你已经不想听并且想给他来上一拳。就这段时间,你一直在反反复复的修改代码,但其实在真实的业务中,可能远远不止这一些,可能还会有更多变动和活动节日的需求,而这些代码的修改或者增加,都是在增加注册模块的代码臃肿,这并不符合高内聚,低耦合的思想,甚至可能造成代码不好维护,从而出现多bug的几率。

二、事件机制

1、事件机制的定义

        那么,为了解决上面的问题,有请我们的主角登场——事件机制。通俗来讲:事件机制就是当某些模块需要关注某个模块的某件事情,那么这些关注的模块就需要监听这件事情,而被关注的模块就需要抛出一个事件来表示这件事件发生了,而后续的操作就交由这些关注的模块进行操作,这是一个一对多的关系,被关注模块的某件事情与关注的各个模块。而聪明的同学就发现了,这不是和观察者模式很接近嘛,其实事件机制就是通过观察者模式实现的,它的主要目的是为了解耦,各个模块之间有数据交互的联系,但各个模块的业务逻辑还是属于自己的。

在一个完整的事件体系中、存在以下的角色:

事件:描述发生了什么事情、比如说完成注册。

事件源:事件的产生者、任何一个事件都必须有一个事件源。

事件广播器:事件和事件监听器的桥梁、负责把事件通知给事件监听器,类似于中介。

事件监听器:监听事件的发生、可以在监听器中做一些处理

2、事件机制的简单实现

下面是事件监听器的代码:

package com.code.hao;

public interface IEventListener {

    void update(Object origin, IEventMessage event);
}
package com.code.hao;

/**
 * 事件监听器
 */
public class SendEmailListener implements IEventListener {

    @Override
    public void update(Object origin, IEventMessage event) {
        if (!(event instanceof UserRegisterEvent)) return;
        UserRegisterEvent userRegisterEvent = (UserRegisterEvent) event;
        System.out.println("用户: " + userRegisterEvent.getUserName() + " 发送短信,推送活动成功");
    }
}

下面是事件的代码:

package com.code.hao;

public interface IEventMessage {
}
package com.code.hao;

/**
 * 事件
 */
public class UserRegisterEvent implements IEventMessage {

    private final String userName;

    public UserRegisterEvent(String userName) {
        this.userName = userName;
    }

    public String getUserName() {
        return userName;
    }
}

下面是事件广播器的代码:

package com.code.hao;

import java.util.*;

/**
 * 事件广播器
 */
public class EventDispatchManager {

    // 用于存放事件与事件监听器的关系
    private Map<String, List<IEventListener>> eventListenerMap = new HashMap<>();

    // 用于注册事件监听器
    public void registerListener(Class<? extends IEventMessage> eventClass, IEventListener listener) {
        String key = eventClass.getName();
        List<IEventListener> eventListeners = eventListenerMap.computeIfAbsent(key, k -> new ArrayList<>());
        eventListeners.add(listener);
    }

    // 用于抛事件
    public void sendEvent(Object origin, IEventMessage eventMessage) {
        String key = eventMessage.getClass().getName();
        List<IEventListener> eventListeners = eventListenerMap.get(key);
        if (eventListeners != null && eventListeners.size() > 0) {
            eventListeners.forEach(listener -> listener.update(origin, eventMessage));
        }
    }
}
package com.code.hao;

/**
 * 为了方便静态方法直接调用事件广播器,而多包装了一层
 */
public class EventSystem {

    private static EventDispatchManager manager = new EventDispatchManager();

    public static void registerListener(Class<? extends IEventMessage> eventClass, IEventListener listener) {
        manager.registerListener(eventClass, listener);
    }

    public static void sendEvent(Object origin, IEventMessage eventMessage) {
        manager.sendEvent(origin, eventMessage);
    }
}

代码测试案例:

package com.code.hao;

public class Main {

    public static void main(String[] args) {
        Main main = new Main();
        main.Test();
    }

    public void Test() {
        // 注册事件
        SendEmailListener listener = new SendEmailListener();
        EventSystem.registerListener(UserRegisterEvent.class, listener);
        // 模拟用户注册成功后
        // 抛事件
        UserRegisterEvent event = new UserRegisterEvent("张三");
        EventSystem.sendEvent(this, event);
    }
}

        以上其实只是很简单很简单的事件机制的实现,因为我们可以发现存在着很多问题:

        1、对每一个事件都需要创建新的监听类并且注册。(其实可以通过注解方式解决,坏笑~)

        2、如果监听多个事件,那么就需要 instance of 进行判断,但这样不符合职责单一原则。

三、总结

       此时有人可能会问,事件机制那么好用,

       1、那是否已经有造好的轮子呢?

       其实在Spring开源框架中,已经自带了这个事件系统组件,而Spring事件机制功能更加强大!它支持实现接口和注解的两种方式,支持同步通知和异步通知,也支持优先级事件的处理,也有一些Spring自带的事件给我们监听。不过我们这里就不对Spring事件机制进行展开说明,主要是浅谈一下事件机制这个概念以及它的作用,感兴趣的同学可以自行去找相关资料。

       2、为什么在常见Web开发感觉很少用过或者没用过呢?

       这是一个好问题,就当留作一个小悬念吧。但是不管怎样,这种思想还是值得我们学习的。

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值