1. 事件处理模型
  2. 事件处理经常用观察者模式+责任链模式
  3. 需要详细掌握
  4. reactor设计模式本质也是observer

情景引入

  • 小朋友睡醒了,饿!——> 哭 ——> 喂奶

UML类图

image.png

处理方式

v1披着面向对象外衣的面向过程

  • 干等着,阻塞,浪费cpu资源 ```java package com.mashibing.dp.observer.v1;

/**

  • 披着面向对象外衣的面向过程 */

public class Main1 { public static void main(String[] args) { boolean cry = false;

  1. while(!cry) {
  2. //进行处理
  3. }
  4. }

}

  1. <a name="CU5ca"></a>
  2. ### v2面向对象的傻等
  3. - 另一个线程调用wakeUp
  4. - 多线程同步
  5. ```java
  6. package com.mashibing.dp.observer.v2;
  7. /**
  8. * 面向对象的傻等
  9. */
  10. class Child {
  11. private boolean cry = false;
  12. public boolean isCry() {
  13. return cry;
  14. }
  15. public void wakeUp() {
  16. System.out.println("Waked Up! Crying wuwuwuwu...");
  17. cry = true;
  18. }
  19. }
  20. public class Main {
  21. public static void main(String[] args) {
  22. Child child = new Child();
  23. while(!child.isCry()) {
  24. try {
  25. Thread.sleep(1000);
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. }
  29. System.out.println("observing...");
  30. }
  31. // 观察者进行相应的处理
  32. }
  33. }

v3加入观察者dad

  • 处理过程放在wakeUp方法中了
  • 将观察者放进被观察者中,触发事件就调用观察者相应的处理方法
  • 耦合度太高,不灵活
  • 想多加几个观察者要修改代码,不符合开闭原则 ```java package com.mashibing.dp.observer.v3;

/**

  • 加入观察者 */

class Child { private boolean cry = false; private Dad d = new Dad();

  1. public boolean isCry() {
  2. return cry;
  3. }
  4. public void wakeUp() {
  5. cry = true;
  6. d.feed();
  7. }

}

class Dad { public void feed() { System.out.println(“dad feeding…”); } }

public class Main { public static void main(String[] args) { Child c = new Child(); //do sth c.wakeUp(); } }

  1. <a name="Us6nK"></a>
  2. ### v4多个观察者
  3. - 加入新的观察者时会很费劲(要改两个地方)
  4. - 不灵活
  5. - 观察者有些方法不想跟被观察者耦合到一起(狗看到陌生人叫,小孩醒了也叫,只作为自己的业务方法)
  6. - 耦合度太高
  7. ```java
  8. package com.mashibing.dp.observer.v4;
  9. /**
  10. * 加入多个观察者
  11. */
  12. class Child {
  13. private boolean cry = false;
  14. private Dad dad = new Dad();
  15. private Mum mum = new Mum();
  16. private Dog dog = new Dog();
  17. public boolean isCry() {
  18. return cry;
  19. }
  20. public void wakeUp() {
  21. cry = true;
  22. dad.feed();
  23. dog.wang();
  24. mum.hug();
  25. }
  26. }
  27. class Dad {
  28. public void feed() {
  29. System.out.println("dad feeding...");
  30. }
  31. }
  32. class Mum {
  33. public void hug() {
  34. System.out.println("mum hugging...");
  35. }
  36. }
  37. class Dog {
  38. public void wang() {
  39. System.out.println("dog wang...");
  40. }
  41. }
  42. public class Main {
  43. public static void main(String[] args) {
  44. Child c = new Child();
  45. //do sth
  46. c.wakeUp();
  47. }
  48. }

v5用存放Observer接口类型的List保存所有观察者

  • 所有的观察者都实现Observer接口
  • 所有的观察者重写actionOnWakeUp,做出动作发出后的相应的动作
  • 观察者模式与责任链模式有很多相似的地方,常常一起用
  • 观察者模式实际上是对多态的灵活运用
  • 而Observer经常串在一起,事件发生时,要经过这些事件的处理才算完事;假如其中有一个处理完之后不想往下传递了,就是责任链模式
  • 观察者模式—->同时发生,但也会有调用的先后顺序
    • 鼠标点击事件首先传给操作系统
    • 操作系统处理
    • 经过一层一层地传递
    • 最终传递到触发鼠标点击事件的窗口
  • 责任链模式能中断,但观察者一般是不中断的

image.png

  1. package com.mashibing.dp.observer.v5;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * 分离观察者与被观察者
  6. */
  7. class Child {
  8. private boolean cry = false;
  9. // 因为有很多观察者并且数量不确定,所以可以用一接口类型的列表来存
  10. // 实现接口必须重写actionOnWakeUp方法让被观察者发生某一动作后通过for循环调用
  11. private List<Observer> observers = new ArrayList<>();
  12. {
  13. observers.add(new Dad());
  14. observers.add(new Mum());
  15. observers.add(new Dog());
  16. }
  17. public boolean isCry() {
  18. return cry;
  19. }
  20. public void wakeUp() {
  21. cry = true;
  22. for(Observer o : observers) {
  23. o.actionOnWakeUp();
  24. }
  25. }
  26. }
  27. interface Observer {
  28. void actionOnWakeUp();
  29. }
  30. class Dad implements Observer {
  31. public void feed() {
  32. System.out.println("dad feeding...");
  33. }
  34. @Override
  35. public void actionOnWakeUp() {
  36. feed();
  37. }
  38. }
  39. class Mum implements Observer {
  40. public void hug() {
  41. System.out.println("mum hugging...");
  42. }
  43. @Override
  44. public void actionOnWakeUp() {
  45. hug();
  46. }
  47. }
  48. class Dog implements Observer {
  49. public void wang() {
  50. System.out.println("dog wang...");
  51. }
  52. @Override
  53. public void actionOnWakeUp() {
  54. wang();
  55. }
  56. }
  57. public class Main {
  58. public static void main(String[] args) {
  59. Child c = new Child();
  60. //do sth
  61. c.wakeUp();
  62. }
  63. }

v6观察者根据被观察者在不同情况下做出不同反应

  • 将事件单独分离出来,成为一个事件类
  • 事件类封装了事件源对象(即被观察者)的情况(child哭的时间、地点等),并传给事件监听者(即观察者)
  • 被观察者要发出事件(fire event)
  • 类比生产者-消费者模式(发布-订阅模式)
    • 被观察者生产一事件,观察者去消费
    • 模式灵活用,不硬套
  • 将事件类型作为actionOnWakeUp的参数传给观察者
  • 有以下几种角色
    • 事件源对象(产生事件对象,也称被观察者)
    • 事件(被观察者发出的事件,包括事件发生的context)
    • 事件监听器(根据事件作出相应的动作,也称观察者,观察事件) ```java package com.mashibing.dp.observer.v6;

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

/**

  • 有很多时候,观察者需要根据事件的具体情况来进行处理 */

class Child { private boolean cry = false; private List observers = new ArrayList<>();

  1. {
  2. observers.add(new Dad());
  3. observers.add(new Mum());
  4. observers.add(new Dog());
  5. }
  6. public boolean isCry() {
  7. return cry;
  8. }
  9. public void wakeUp() {
  10. cry = true;
  11. wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed");
  12. for(Observer o : observers) {
  13. o.actionOnWakeUp(event);
  14. }
  15. }

}

//事件类 fire Event class wakeUpEvent{ long timestamp; String loc;

  1. public wakeUpEvent(long timestamp, String loc) {
  2. this.timestamp = timestamp;
  3. this.loc = loc;
  4. }

}

interface Observer { void actionOnWakeUp(wakeUpEvent event); }

class Dad implements Observer { public void feed() { System.out.println(“dad feeding…”); }

  1. @Override
  2. public void actionOnWakeUp(wakeUpEvent event) {
  3. feed();
  4. }

}

class Mum implements Observer { public void hug() { System.out.println(“mum hugging…”); }

  1. @Override
  2. public void actionOnWakeUp(wakeUpEvent event) {
  3. hug();
  4. }

}

class Dog implements Observer { public void wang() { System.out.println(“dog wang…”); }

  1. @Override
  2. public void actionOnWakeUp(wakeUpEvent event) {
  3. wang();
  4. }

}

public class Main { public static void main(String[] args) { Child c = new Child(); //do sth c.wakeUp(); } }

  1. <a name="isrRi"></a>
  2. ### v7观察者获取事件源对象
  3. - 观察者有时候需要知道事件源对象(根据不同的对象进行处理)
  4. - why?because观察者需要根据事件源对象进行不同的处理或者观察者的处理过程中需要用到事件源对象中的一些资源
  5. - 事件源可以发出好多不同的动作
  6. - 观察者的动作和事件源本身并不是完完全全的耦合在一起
  7. - 观察者面对不同的动作时可以采取相同的动作
  8. - 一个观察者可以监听在多个事件源上
  9. - 事件源也可以发出好多个不同的事件
  10. - 事件源、事件和事件监听器之间的耦合并不是那么强(有耦合-->List集合)
  11. - ❓spring中的aop切面,观察者观察在切面上,spring中的耦合度更低?
  12. - 语义上是一个Observer(咬文嚼字)
  13. - spring中的事件源对象没必要维护一个观察者(监听器)集合,而是通过配置文件切进去即可
  14. - 本质上是一个代理
  15. - 解决方案:
  16. - 首先不能将事件源对象直接作为观察者的成员变量(耦合度太高了--->只能观察一个对象,观察了小狗就不能观察小鸟了)
  17. - 将事件源对象放进事件event中去--->source,观察者需要用就通过event.getSource调用
  18. ```java
  19. package com.mashibing.dp.observer.v7;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. /**
  23. * 有很多时候,观察者需要根据事件的具体情况来进行处理
  24. * 大多数时候,我们处理事件的时候,需要事件源对象
  25. */
  26. class Child {
  27. private boolean cry = false;
  28. private List<Observer> observers = new ArrayList<>();
  29. {
  30. observers.add(new Dad());
  31. observers.add(new Mum());
  32. observers.add(new Dog());
  33. }
  34. public boolean isCry() {
  35. return cry;
  36. }
  37. public void wakeUp() {
  38. cry = true;
  39. // 将被观察者自身传进event中
  40. wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed", this);
  41. for(Observer o : observers) {
  42. o.actionOnWakeUp(event);
  43. }
  44. }
  45. }
  46. class wakeUpEvent{
  47. long timestamp;
  48. String loc;
  49. Child source;
  50. public wakeUpEvent(long timestamp, String loc, Child source) {
  51. this.timestamp = timestamp;
  52. this.loc = loc;
  53. this.source = source;
  54. }
  55. //获取事件源对象
  56. public Child getSource() {
  57. return source;
  58. }
  59. // 其他的set、get方法
  60. }
  61. interface Observer {
  62. void actionOnWakeUp(wakeUpEvent event);
  63. }
  64. class Dad implements Observer {
  65. public void feed() {
  66. System.out.println("dad feeding...");
  67. }
  68. @Override
  69. public void actionOnWakeUp(wakeUpEvent event) {
  70. feed();
  71. }
  72. }
  73. class Mum implements Observer {
  74. public void hug() {
  75. System.out.println("mum hugging...");
  76. }
  77. @Override
  78. public void actionOnWakeUp(wakeUpEvent event) {
  79. hug();
  80. }
  81. }
  82. class Dog implements Observer {
  83. public void wang() {
  84. System.out.println("dog wang...");
  85. }
  86. @Override
  87. public void actionOnWakeUp(wakeUpEvent event) {
  88. wang();
  89. }
  90. }
  91. public class Main {
  92. public static void main(String[] args) {
  93. Child c = new Child();
  94. //do sth
  95. c.wakeUp();
  96. }
  97. }

v8事件的继承体系

  • Event抽象类作为父类
  • T为事件源对象的类型
  • 抽象类中有一抽象方法:abstract T getSource();
  • 主要目的就是为了使getSource更加通用 ```java package com.mashibing.dp.observer.v8;

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

/**

  • 有很多时候,观察者需要根据事件的具体情况来进行处理
  • 大多数时候,我们处理事件的时候,需要事件源对象
  • 事件也可以形成继承体系 */

class Child { private boolean cry = false; private List observers = new ArrayList<>();

  1. {
  2. observers.add(new Dad());
  3. observers.add(new Mum());
  4. observers.add(new Dog());
  5. observers.add((e)->{
  6. System.out.println("ppp");
  7. });
  8. //hook callback function
  9. }
  10. public boolean isCry() {
  11. return cry;
  12. }
  13. public void wakeUp() {
  14. cry = true;
  15. wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed", this);
  16. for(Observer o : observers) {
  17. // 事件沿一个一个观察者往下传(Observer可以决定是否向下传?返回值或者第三个参数)
  18. // 上一条注释详见责任链模式
  19. o.actionOnWakeUp(event);
  20. }
  21. }

}

abstract class Event { abstract T getSource(); }

class wakeUpEvent extends Event{ long timestamp; String loc; Child source;

  1. public wakeUpEvent(long timestamp, String loc, Child source) {
  2. this.timestamp = timestamp;
  3. this.loc = loc;
  4. this.source = source;
  5. }
  6. @Override
  7. Child getSource() {
  8. return source;
  9. }
  10. // 其他的set、get方法

}

interface Observer { void actionOnWakeUp(wakeUpEvent event); }

class Dad implements Observer { public void feed() { System.out.println(“dad feeding…”); }

  1. @Override
  2. public void actionOnWakeUp(wakeUpEvent event) {
  3. feed();
  4. }

}

class Mum implements Observer { public void hug() { System.out.println(“mum hugging…”); }

  1. @Override
  2. public void actionOnWakeUp(wakeUpEvent event) {
  3. hug();
  4. }

}

class Dog implements Observer { public void wang() { System.out.println(“dog wang…”); }

  1. @Override
  2. public void actionOnWakeUp(wakeUpEvent event) {
  3. wang();
  4. }

}

public class Main { public static void main(String[] args) { Child c = new Child(); //do sth c.wakeUp(); } }

  1. <a name="qOo7S"></a>
  2. ### v9Java AWT自带的观察者模式
  3. - 那些方法是由AWT框架本身帮你调
  4. - jvm监听os的键盘事件,生成KeyEvent对象,调用keyPressed方法(即触发了事件)
  5. - os监听到键盘事件,发现是个java程序,便将产生的键盘事件交给jvm,最后由jvm调用监听器的处理方法
  6. ```java
  7. package com.mashibing.dp.observer.v9;
  8. import java.awt.Button;
  9. import java.awt.Frame;
  10. import java.awt.event.ActionEvent;
  11. import java.awt.event.ActionListener;
  12. import java.awt.event.WindowAdapter;
  13. import java.awt.event.WindowEvent;
  14. public class TestFrame extends Frame {
  15. public void launch() {
  16. Button b = new Button("press me");
  17. b.addActionListener(new MyActionListener());
  18. b.addActionListener(new MyActionListener2());
  19. this.add(b);
  20. // 使按钮充满Frame窗口
  21. this.pack();
  22. this.addWindowListener(new WindowAdapter(){
  23. @Override
  24. public void windowClosing(WindowEvent e) {
  25. System.exit(0);
  26. }
  27. });
  28. // 设定大小
  29. this.setLocation(400, 400);
  30. this.setVisible(true);
  31. }
  32. public static void main(String[] args) {
  33. new TestFrame().launch();
  34. }
  35. private class MyActionListener implements ActionListener { //Observer
  36. @Overwrite
  37. public void actionPerformed(ActionEvent e) {
  38. ((Button)e.getSource()).setLabel("press me again!");
  39. System.out.println("button pressed!");
  40. }
  41. }
  42. private class MyActionListener2 implements ActionListener {
  43. @Overwrite
  44. public void actionPerformed(ActionEvent e) {
  45. System.out.println("button pressed 2!");
  46. }
  47. }
  48. }

v10javascript事件

认识javascript event对象

  1. <script type="text/javascript">
  2. function handle() {
  3. alert(event.target.value);
  4. }
  5. </script>
  6. <input type="button" value="press me" onclick="handle()"/>

v11observer与责任链共用

在很多系统中,Observer模式往往和责任链共同负责对于事件的处理, 其中的某一个observer负责是否将事件进一步传递或者不传递 本质就是事件对象event穿过Observer的链条 不会往回传(责任链中的response可以往回传)

钩子函数(hook callback function)

  • 本质上就是Observer
  • 1.8后可以用lambda表达式传入函数对象(js、python、c++、……都可以)
  • 相当于把方法传过去;只不过之前java中不支持,所以传的是接口
  • hook、callback、listener、observer说的是一回事

🤏随想

  1. 内部类(嵌套类)可以直接使用外部类的资源
  2. 关于事件的处理一般都跟observer有关
  3. observer往往和责任链共同负责对事件的处理,其中的某一个observer负责决定是否将事件进一步传递(但无法往回传,责任链可以)