一文搞懂Java事件委托(观察者模式的拓展)

Bertha 。 2021-06-10 20:38 327阅读 0赞

引言:

  1. 本文的前提是你要了解一下观察者模式的结构。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zNzk2ODYxMw_size_16_color_FFFFFF_t_70

这是《大话设计模式》中关于观察者模式的结构图,两个抽象类两个具体类。Subject是通知者,Observer是观察者,当通知者执行Notify方法时,Notify方法会循环执行所有观察者的Update方法。实现一个通知执行的功能。如果你有一个需求,当事件A发生时,相应的事件B、事件C也会相应执行,但要通知的事件数量和内容都不固定,且与事件A逻辑关系不大,那么把事件BC写在事件A中就不合理了,此时采用观察者模式是个不错的选择。

观察者模式的缺点:

通过结构图可以看到,通知者需要包含Observer接口对象,相耦合,另外,所有的通知者都需要实现Observer接口并实现update()方法,这是很拘板的。全新开发的项目还好,关联老项目就麻烦了,比如当用户注册成功时需要给他发送一封邮件,而发送邮件方法是已有的

  1. public Class EmailUtil {
  2. public sendEmail(){
  3. //do something
  4. }
  5. }

如果我们用观察者模式实现,必须

  1. public Class EmailUtil implements Observer{
  2. public sendEmail(){
  3. //do something
  4. }
  5. @Override
  6. public void update() {
  7. this.sendEamil();
  8. }
  9. }

或者要新建一个观察者类来实现Observer接口,是不是很麻烦,为啥不可以直接委托给sendEmail()呢?

事件委托登场:

  1. 大家好,我是事件委托,之所以我那么成功还是得感谢java反射,否则我将一事无成。其实我做的东西很简单,原本我是调用Observer.update方法,现在改为Object.getClass().getMethod().invoke(object,params)。这样就解决了必须要实现Observer接口实现update()方法的问题了。

我们来看看现在的Observer,现在定义成一个事件:

  1. package designPattern.Observer_delegate;
  2. import java.lang.reflect.Method;
  3. /**
  4. * <pre>类名: Event</pre>
  5. * <pre>描述: 通过反射,实现对事件的封装</pre>
  6. * <pre>版权: web_chen@163.com</pre>
  7. * <pre>日期: 2020/3/19 14:19</pre>
  8. * <pre>作者: chenwb</pre>
  9. */
  10. public class Event {
  11. private Object target;
  12. private String methodName;
  13. private Object[] params;
  14. private Class[] paramsTypes;
  15. public Event(Object target, String methodName, Object[] params) {
  16. this.target = target;
  17. this.methodName = methodName;
  18. this.params = params;
  19. createParamsTypes(params);
  20. }
  21. /**
  22. * @Description: 获取参数类型,反射时用
  23. * @author chenwb
  24. * @date 2020/3/19 14:55
  25. * @param params
  26. */
  27. private void createParamsTypes(Object[] params) {
  28. paramsTypes = new Class[params.length];
  29. for (int i = 0; i < params.length; i++) {
  30. paramsTypes[i] = params[i].getClass();
  31. }
  32. }
  33. /**
  34. * @Description: invoke方法,就是Observer的update方法
  35. * @author chenwb
  36. * @date 2020/3/19 14:55
  37. */
  38. public void invoke() {
  39. Method method = null;
  40. try {
  41. method = target.getClass().getMethod(methodName, paramsTypes);
  42. method.invoke(target, params);
  43. } catch (Exception e) {
  44. System.out.println("委托执行异常,异常原因:" + e.getMessage());
  45. // no to do
  46. }
  47. }
  48. }

现在的通知者:

  1. package designPattern.Observer_delegate;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * <pre>类名: EventHandler</pre>
  6. * <pre>描述: 事件执行器,封装事件</pre>
  7. * <pre>版权: web_chen@163.com</pre>
  8. * <pre>日期: 2020/3/19 14:19</pre>
  9. * <pre>作者: chenwb</pre>
  10. */
  11. public class EventHandler {
  12. private List<Event> eventList = new ArrayList<>();
  13. public boolean addEvent(Event event) {
  14. return eventList.add(event);
  15. }
  16. /**
  17. * @Description: 添加委托事件
  18. * @author chenwb
  19. * @date 2020/3/19 14:20
  20. * @param object 事件执行目标
  21. * @param methodName 事件执行方法
  22. * @param args 执行参数
  23. */
  24. public void addEvent(Object object, String methodName, Object... args) {
  25. eventList.add(new Event(object, methodName, args));
  26. }
  27. /**
  28. * @Description: 事件循环执行
  29. * @author chenwb
  30. * @date 2020/3/19 14:21
  31. */
  32. public void handle() {
  33. for (Event e : eventList) {
  34. e.invoke();
  35. }
  36. }
  37. }

为了让具体的业务类更加简单,我们把通知这件事抽成抽象类:

  1. package designPattern.Observer_delegate;
  2. /**
  3. * <pre>类名: DefaultDelegation</pre>
  4. * <pre>描述: 默认委托,提供了默认实现</pre>
  5. * <pre>版权: web_chen@163.com</pre>
  6. * <pre>日期: 2020/3/19 14:19</pre>
  7. * <pre>作者: chenwb</pre>
  8. */
  9. public abstract class DefaultDelegation {
  10. private EventHandler eventHandler = new EventHandler();
  11. /**
  12. * @Description: 新增委托
  13. * @author chenwb
  14. * @date 2020/3/19 13:46
  15. * @param target 目标类
  16. * @param methodName 目标方法
  17. * @param args args 可变入参
  18. * @return void
  19. */
  20. public void addListeners(Object target, String methodName, Object... args) {
  21. eventHandler.addEvent(target, methodName, args);
  22. }
  23. /**
  24. * @Description: 通知他人
  25. * @author chenwb
  26. * @date 2020/3/19 14:17
  27. * @return void
  28. */
  29. public void notifyOthers() {
  30. eventHandler.handle();
  31. }
  32. /**
  33. * 获取eventHandler
  34. * @return eventHandler
  35. */
  36. public EventHandler getEventHandler() {
  37. return eventHandler;
  38. }
  39. /**
  40. * 设置eventHandler
  41. * @param eventHandler eventHandler
  42. */
  43. public void setEventHandler(EventHandler eventHandler) {
  44. this.eventHandler = eventHandler;
  45. }
  46. }

以基民委托基金经理的买股票为例,继承通知的逻辑:

  1. package designPattern.Observer_delegate;
  2. /**
  3. * <pre>类名: FundInvestor</pre>
  4. * <pre>描述: 基民(买基金的人)</pre>
  5. * <pre>版权: web_chen@163.com</pre>
  6. * <pre>日期: 2020/3/19 14:22</pre>
  7. * <pre>作者: chenwb</pre>
  8. */
  9. public class FundInvestor extends DefaultDelegation {
  10. private String name;
  11. public FundInvestor(String name) {
  12. this.name = name;
  13. }
  14. /**
  15. * @Description: 买基金
  16. * @author chenwb
  17. * @date 2020/3/19 14:22
  18. */
  19. public void buyFund() {
  20. System.out.println(name + "买基金!");
  21. //委托事件 可以委托任何事件
  22. notifyOthers();
  23. }
  24. }

基金经理(正常逻辑,不需要事先事先Observer接口,与基民是没有耦合的):

  1. package designPattern.Observer_delegate;
  2. /**
  3. * <pre>类名: FundManager</pre>
  4. * <pre>描述: 基金经理</pre>
  5. * <pre>版权: web_chen@163.com</pre>
  6. * <pre>日期: 2020/3/19 14:22</pre>
  7. * <pre>作者: chenwb</pre>
  8. */
  9. public class FundManager {
  10. private String name;
  11. public FundManager(String name) {
  12. this.name = name;
  13. }
  14. /**
  15. * @Description: 买股票A
  16. * @author chenwb
  17. * @date 2020/3/19 14:22
  18. */
  19. public void buyStockA() {
  20. System.out.println(name + "经理买股票A");
  21. }
  22. /**
  23. * @Description: 买股票B
  24. * @author chenwb
  25. * @date 2020/3/19 14:22
  26. */
  27. public void buyStockB() {
  28. System.out.println(name + "经理买股票B");
  29. }
  30. }

客户端类:

  1. package designPattern.Observer_delegate;
  2. public class TradeCenter {
  3. public static void main(String[] args) {
  4. FundInvestor fundInvestor = new FundInvestor("小张");
  5. FundManager fundManager = new FundManager("天弘");
  6. fundInvestor.addListeners(fundManager,"buyStockA");
  7. fundInvestor.addListeners(fundManager,"buyStockB");
  8. fundInvestor.buyFund();
  9. }
  10. }

如果需要买股票A,只要绑定buyStockA方法即可。

20200319151243369.png

相比观察者模式,事件委托相对更加灵活。

发表评论

表情:
评论列表 (有 0 条评论,327人围观)

还没有评论,来说两句吧...

相关阅读

    相关 委托观察模式

    委托(`delegate`)是一种特殊的类型(`class`),它可以被认为是一个可以拥有函数引用的类,它的声明规定了它能够持有的函数引用的函数形式,同时它可以存储多个函数引用

    相关 访问模式

    关于设计模式,我们得结合生活中的案例来学习;最近我在网上也看了不少文章,今天想跟大家分享一下关于访问者模式的一些知识,先来看一个简单的案例吧。 相信大家都去过医院,看完病,医