0.参考资料



1.概述

  • 定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。 ——《设计模式》GoF

1.1动机

  1. - 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系” ——一个对象(目标对象)的状态发生改变,所有依赖于它的对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。

1.2结构

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629376116823-d24f0e21-9fa1-4a7b-b5f5-026e7ace2e25.png#clientId=uf87aa520-1853-4&from=paste&height=222&id=ub90e7840&margin=%5Bobject%20Object%5D&name=image.png&originHeight=443&originWidth=1150&originalType=binary&ratio=1&size=135794&status=done&style=none&taskId=uac065438-20bf-480e-bcd1-40839aa2f23&width=575)

2.要点总结

宏观架构

  1. 1. 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。
  2. 1. 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。
  3. 1. 观察者自己决定是否需要订阅通知,目标对象对此一无所知。
  4. 1. Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。(JavaWeb中的Listener)

微观代码

  1. 1. <br />
  2. 1. <br />
  3. 1. <br />

3.案例

需求

  1. - 天气预报项目需求,具体要求如下:
  2. - 1) 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
  3. - 2) 需要设计开放型API,便于其他第三方也能接入气象站获取数据。
  4. - 3) 提供温度、气压和湿度的接口(模拟数据为: detail1, detail2...)
  5. - 4) 测量数据更新时,要能实时的通知给第三方

示意图

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629386592597-e3c75677-df7e-40a8-a657-f40e67ab7d56.png#clientId=u57f8af3d-ace3-4&from=paste&height=234&id=u978c7d9d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=467&originWidth=775&originalType=binary&ratio=1&size=73431&status=done&style=none&taskId=u4a83a751-f274-4f62-8b42-6684f502ed2&width=387.5)

代码

  1. - (伪)被观察者
  1. public class WeatherData {
  2. private int detail1;
  3. private int detail2;
  4. private int detail3;
  5. MyObserver subject;
  6. public WeatherData(MyObserver observer){
  7. this.subject = observer;
  8. }
  9. public WeatherData() {
  10. }
  11. void dataChange(int detail1, int detail2, int detail3){
  12. this.detail1 = detail1;
  13. this.detail2 = detail2;
  14. this.detail3 = detail3;
  15. subject.update(detail1,detail2,detail3);
  16. }
  17. }
  1. - (伪)观察者
  1. public class MyObserver {
  2. private int detail1;
  3. private int detail2;
  4. private int detail3;
  5. public void update(int detail1, int detail2, int detail3){
  6. this.detail1 = detail1;
  7. this.detail2 = detail2;
  8. this.detail3 = detail3;
  9. show();
  10. }
  11. public void show(){
  12. System.out.println("已更新...");
  13. System.out.println("detail1: " + detail1);
  14. System.out.println("detail2: " + detail2);
  15. System.out.println("detail3: " + detail3);
  16. }
  17. }
  1. - 测试及结果
  1. public class Client {
  2. public static void main(String[] args) {
  3. WeatherData weatherData = new WeatherData(new MyObserver());
  4. weatherData.dataChange(10, 20, 30);
  5. weatherData.dataChange(100, 200, 300);
  6. }
  7. }
  1. 已更新...
  2. detail1: 10
  3. detail2: 20
  4. detail3: 30
  5. 已更新...
  6. detail1: 100
  7. detail2: 200
  8. detail3: 300

分析

  1. - 问题:
  2. - 若需要给其它第三方接入气象站获取数据...
  3. - 无法在运行时动态的添加第三方 (新浪网站)
  4. - 违反ocp原则 =>观察者模式

4.使用模式

  1. - 观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为ObserverSubject通知Observer变化,比如这里的天气监测站是Subject,是1的一方。用户时Observer,是多的一方

方案

  1. - 气象局:Subject(主体)
  2. - 功能: 登记注册、移除和通知
  3. - registerObserver 注册
  4. - removeObserver 移除
  5. - notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用 户来取,也可能是实施推送,看具体需求定
  6. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629386955851-ac154317-e2ff-4a19-bf75-951655e8daa1.png#clientId=u57f8af3d-ace3-4&from=paste&id=u4baba07b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=136&originWidth=128&originalType=binary&ratio=1&size=3555&status=done&style=none&taskId=u36cd739b-8c22-424a-8eac-6561b49f6d0)
  7. - 用户/第三方网站:Observer(观察者)
  8. - 功能: 接收更新
  9. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629387001113-30dcf819-6dbf-4627-a1b4-d2db6d06ee2e.png#clientId=u57f8af3d-ace3-4&from=paste&id=u01252f5b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=98&originWidth=128&originalType=binary&ratio=1&size=1983&status=done&style=none&taskId=u0a7bfbbe-b0c2-414f-b7e0-8de35afa87f)

类图

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629387058673-7b4a7d5c-cc87-42f7-9e15-d1b00ab28a38.png#clientId=u57f8af3d-ace3-4&from=paste&height=345&id=u5235f397&margin=%5Bobject%20Object%5D&name=image.png&originHeight=459&originWidth=650&originalType=binary&ratio=1&size=24021&status=done&style=none&taskId=u4fbf39f2-d6c3-43a6-a39d-96299882095&width=488)

代码

  1. - Subject(主体, 天气监测站)
  1. // 抽象主体
  2. public interface InterfaceSubject {
  3. void registerObserver(InterfaceObserver observer);
  4. void removeObserver(InterfaceObserver observer);
  5. void notifyObserver();
  6. void update(int detail1, int detail2);
  7. }
  8. // 具体的一个主体类
  9. public class ConcreteSubject1 implements InterfaceSubject {
  10. // 基础的需要更新(推送)的信息
  11. int detail1;
  12. int detail2;
  13. // 观察者集合
  14. ArrayList<InterfaceObserver> observers;
  15. public ConcreteSubject1(){
  16. this.observers = new ArrayList<InterfaceObserver>();
  17. }
  18. @Override
  19. public void registerObserver(InterfaceObserver observer) {
  20. System.out.println("主体 新增注册中...");
  21. observers.add(observer);
  22. System.out.println("主体 新增注册成功!");
  23. }
  24. @Override
  25. public void removeObserver(InterfaceObserver observer) {
  26. System.out.println("主体 移除注册中...");
  27. if (observers.contains(observer)){
  28. observers.remove(observer);
  29. System.out.println("主体 移除注册成功!");
  30. } else {
  31. System.out.println("主体 移除注册失败....目标不存在!");
  32. }
  33. }
  34. // 推送
  35. @Override
  36. public void notifyObserver() {
  37. System.out.println("主体 开始全局推送");
  38. for (InterfaceObserver observer : observers) {
  39. observer.update(this.detail1, this.detail2);
  40. }
  41. }
  42. // 主体更新数据
  43. public void update(int detail1, int detail2) {
  44. this.detail1 = detail1;
  45. this.detail2 = detail2;
  46. System.out.println("主体 更新数据成功");
  47. // 主动调用, 自动推送
  48. notifyObserver();
  49. }
  50. }
  1. - Observer(观察者)
  1. // 观察者基类
  2. public interface InterfaceObserver {
  3. // 本案例由主体调用, 推送更新
  4. void update(int detail1, int detail2);
  5. }
  6. // 具体的观察者(一号
  7. public class ConcreteObserver1 implements InterfaceObserver {
  8. // 需要更新的具体数据
  9. int detail1;
  10. int detail2;
  11. /**
  12. * 更新天气情况, 使用推送模式, 由 Subject主体 推送
  13. * @param detail1
  14. * @param detail2
  15. */
  16. @Override
  17. public void update(int detail1, int detail2) {
  18. System.out.println("观察者1开始更新...");
  19. this.detail1 = detail1;
  20. this.detail2 = detail2;
  21. System.out.println("观察者1更新成功!");
  22. show();
  23. }
  24. /**
  25. * 展示, 不同的观察者可以有不同的展示样式
  26. */
  27. public void show() {
  28. System.out.println("观察者1进行 [环形图] 展示: ");
  29. System.out.println("detail1: " + detail1);
  30. System.out.println("detail2: " + detail2);
  31. }
  32. }
  33. // 观察者二号
  34. public class ConcreteObserver2 implements InterfaceObserver {
  35. int detail1;
  36. int detail2;
  37. @Override
  38. public void update(int detail1, int detail2) {
  39. System.out.println("观察者2开始更新...");
  40. this.detail1 = detail1;
  41. this.detail2 = detail2;
  42. System.out.println("观察者2更新成功!");
  43. show();
  44. }
  45. public void show() {
  46. System.out.println("观察者2进行 [折线图] 展示: ");
  47. System.out.println("detail1: " + detail1);
  48. System.out.println("detail2: " + detail2);
  49. }
  50. }
  1. - 测试
  1. public class Client {
  2. public static void main(String[] args) {
  3. // 创建主体对象
  4. InterfaceSubject subject = new ConcreteSubject1();
  5. // 创建多个观察者对象
  6. InterfaceObserver observer1 = new ConcreteObserver1();
  7. InterfaceObserver observer2 = new ConcreteObserver2();
  8. // 主体对象注册观察者
  9. subject.registerObserver(observer1);
  10. subject.registerObserver(observer2);
  11. // 主体更新数据(内部自动推送)
  12. subject.update(10,20);
  13. subject.update(100,200);
  14. // 模拟移除注册
  15. subject.removeObserver(observer1);
  16. // 测试移除结果
  17. subject.update(999,123);
  18. }
  19. }
  1. 主体 新增注册中...
  2. 主体 新增注册成功!
  3. 主体 新增注册中...
  4. 主体 新增注册成功!
  5. 主体 更新数据成功
  6. 主体 开始全局推送
  7. 观察者1开始更新...
  8. 观察者1更新成功!
  9. 观察者1进行 [环形图] 展示:
  10. detail1: 10
  11. detail2: 20
  12. 观察者2开始更新...
  13. 观察者2更新成功!
  14. 观察者2进行 [折线图] 展示:
  15. detail1: 10
  16. detail2: 20
  17. 主体 更新数据成功
  18. 主体 开始全局推送
  19. 观察者1开始更新...
  20. 观察者1更新成功!
  21. 观察者1进行 [环形图] 展示:
  22. detail1: 100
  23. detail2: 200
  24. 观察者2开始更新...
  25. 观察者2更新成功!
  26. 观察者2进行 [折线图] 展示:
  27. detail1: 100
  28. detail2: 200
  29. 主体 移除注册中...
  30. 主体 移除注册成功!
  31. 主体 更新数据成功
  32. 主体 开始全局推送
  33. 观察者2开始更新...
  34. 观察者2更新成功!
  35. 观察者2进行 [折线图] 展示:
  36. detail1: 999
  37. detail2: 123

分析

  1. - 观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除 和通知。
  2. - 这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类WeatherData不会修改代码,遵守了ocp原则

5.经典使用


5.1Jdk的Observable类和Observer接口

  1. - Observable Observer 的使用方法和前面讲过的一样,只是Observable 是类,通过继承来实现观察者模式

Observer接口:

  1. - Observer 的作用和地位等价于我们前面讲过的 Observer, update

:::info public interface Observer {__ _void update(Observable o, Object arg);
} :::

Observable类:

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629388830380-1943b0a3-c004-4e77-8994-b119601ebf57.png#clientId=u04519dcc-ccae-4&from=paste&height=92&id=u0992a89b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=184&originWidth=599&originalType=binary&ratio=1&size=13889&status=done&style=none&taskId=u626138f3-962b-4a51-91c1-adf198ee618&width=299.5)
  2. - 分析
  3. - Observable 的作用和地位等价于前面的Subject
  4. - Observable 是类,不是接口,类中已经实现了核心的方法 ,即管理Observer的方法 add.. delete .. notify...