设计模式之观察者模式

£神魔★判官ぃ 2022-03-27 06:22 353阅读 0赞

定义

当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对象状态。观察者模式提供给关联对象一种同步通信的手段,使某个对象与依赖它的其他对象之间保持状态同步

发生改变的对象称之为观察目标,而被通知的对象称之为观察者。一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,所以可以根据需要增加和删除观察者,使得系统更易于扩展。

观察者模式又称为发布-订阅模式。

888fab65b148c48bae753fd477703c44.png

模式包含四个角色

  • Subject抽象被观察者角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
  • Observer抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
  • ConcreteSubject具体被观察者角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知(Callback)。
  • ConcreteObserver具体观察者角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。

实例:

气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
需要设计开放型API,便于其他第三方也能接入气象站获取数据。
提供温度、气压和湿度的接口
测量数据更新时,要能实时的通知给第三方

类图说明

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3NDUwMDg5_size_16_color_FFFFFF_t_70

Subject接口提供了注册、移除、通知Observer 的方法

WeatherData实现Subject

* 1. 包含最新的天气情况信息
* 2. 含有观察者集合,使用ArrayList 管理
* 3. 当数据有更新时,就主动的调用ArrayList, 通知所有的(接入方)就看到最新的信息

Observer有update方法更新天气信息

代码实现

  1. package com.dongguo.observer;
  2. /**
  3. * @author Dongguo
  4. * @date 2021/8/23 0023-9:07
  5. * @description: 观察者接口,有观察者来实现
  6. */
  7. public interface Observer {
  8. public void update(float temperature, float pressure, float humidity);
  9. }
  10. package com.dongguo.observer;
  11. /**
  12. * @author Dongguo
  13. * @date 2021/8/23 0023-9:05
  14. * @description: 接口, 让WeatherData 来实现
  15. */
  16. public interface Subject {
  17. public void registerObserver(Observer o);
  18. public void removeObserver(Observer o);
  19. public void notifyObservers();
  20. }
  21. package com.dongguo.observer;
  22. /**
  23. * @author Dongguo
  24. * @date 2021/8/23 0023-9:08
  25. * @description:
  26. */
  27. public class BaiduSite implements Observer {
  28. // 温度,气压,湿度
  29. private float temperature;
  30. private float pressure;
  31. private float humidity;
  32. // 更新天气情况,是由WeatherData 来调用,我使用推送模式
  33. public void update(float temperature, float pressure, float humidity) {
  34. this.temperature = temperature;
  35. this.pressure = pressure;
  36. this.humidity = humidity;
  37. display();
  38. }
  39. // 显示
  40. public void display() {
  41. System.out.println("===百度网站====");
  42. System.out.println("***百度网站气温: " + temperature + "***");
  43. System.out.println("***百度网站气压: " + pressure + "***");
  44. System.out.println("***百度网站湿度: " + humidity + "***");
  45. }
  46. }
  47. package com.dongguo.observer;
  48. /**
  49. * @author Dongguo
  50. * @date 2021/8/23 0023-9:09
  51. * @description:
  52. */
  53. public class CurrentConditions implements Observer {
  54. // 温度,气压,湿度
  55. private float temperature;
  56. private float pressure;
  57. private float humidity;
  58. // 更新天气情况,是由WeatherData 来调用,我使用推送模式
  59. public void update(float temperature, float pressure, float humidity) {
  60. this.temperature = temperature;
  61. this.pressure = pressure;
  62. this.humidity = humidity;
  63. display();
  64. }
  65. // 显示
  66. public void display() {
  67. System.out.println("***Today mTemperature: " + temperature + "***");
  68. System.out.println("***Today mPressure: " + pressure + "***");
  69. System.out.println("***Today mHumidity: " + humidity + "***");
  70. }
  71. }
  72. package com.dongguo.observer;
  73. import java.util.ArrayList;
  74. /**
  75. * @author Dongguo
  76. * @date 2021/8/23 0023-9:10
  77. * @description:
  78. */
  79. public class WeatherData implements Subject {
  80. private float temperatrue;
  81. private float pressure;
  82. private float humidity;
  83. //观察者集合
  84. private ArrayList<Observer> observers;
  85. //加入新的第三方
  86. public WeatherData() {
  87. observers = new ArrayList<Observer>();
  88. }
  89. public float getTemperature() {
  90. return temperatrue;
  91. }
  92. public float getPressure() {
  93. return pressure;
  94. }
  95. public float getHumidity() {
  96. return humidity;
  97. }
  98. public void dataChange() {
  99. //调用接入方的update
  100. notifyObservers();
  101. }
  102. //当数据有更新时,就调用setData
  103. public void setData(float temperature, float pressure, float humidity) {
  104. this.temperatrue = temperature;
  105. this.pressure = pressure;
  106. this.humidity = humidity;
  107. //调用dataChange, 将最新的信息推送给接入方currentConditions
  108. dataChange();
  109. }
  110. //注册一个观察者
  111. @Override
  112. public void registerObserver(Observer o) {
  113. observers.add(o);
  114. }
  115. //移除一个观察者
  116. @Override
  117. public void removeObserver(Observer o) {
  118. if (observers.contains(o)) {
  119. observers.remove(o);
  120. }
  121. }
  122. //遍历所有的观察者,并通知
  123. @Override
  124. public void notifyObservers() {
  125. for (int i = 0; i < observers.size(); i++) {
  126. observers.get(i).update(this.temperatrue, this.pressure, this.humidity);
  127. }
  128. }
  129. }
  130. package com.dongguo.observer;
  131. /**
  132. * @author Dongguo
  133. * @date 2021/8/23 0023-9:12
  134. * @description:
  135. */
  136. public class Client {
  137. public static void main(String[] args) {
  138. //创建一个WeatherData
  139. WeatherData weatherData = new WeatherData();
  140. //创建观察者
  141. CurrentConditions currentConditions = new CurrentConditions();
  142. BaiduSite baiduSite = new BaiduSite();
  143. //注册到weatherData
  144. weatherData.registerObserver(currentConditions);
  145. weatherData.registerObserver(baiduSite);
  146. //测试
  147. System.out.println("通知各个注册的观察者, 看看信息");
  148. weatherData.setData(10f, 100f, 30.3f);
  149. weatherData.removeObserver(currentConditions);
  150. //测试
  151. System.out.println();
  152. System.out.println("通知各个注册的观察者, 看看信息");
  153. weatherData.setData(10f, 100f, 30.3f);
  154. }
  155. }
  156. 运行结果:
  157. 通知各个注册的观察者, 看看信息
  158. ***Today mTemperature: 10.0***
  159. ***Today mPressure: 100.0***
  160. ***Today mHumidity: 30.3***
  161. ===百度网站====
  162. ***百度网站气温: 10.0***
  163. ***百度网站气压: 100.0***
  164. ***百度网站湿度: 30.3***
  165. 通知各个注册的观察者, 看看信息
  166. ===百度网站====
  167. ***百度网站气温: 10.0***
  168. ***百度网站气压: 100.0***
  169. ***百度网站湿度: 30.3***

观察者模式在Jdk 应用的源码分析

Jdk 的Observable 类就使用了观察者模式

  1. package java.util;
  2. public class Observable {
  3. private boolean changed = false;
  4. private Vector<Observer> obs;
  5. public Observable() {
  6. obs = new Vector<>();
  7. }
  8. public synchronized void addObserver(Observer o) {
  9. if (o == null)
  10. throw new NullPointerException();
  11. if (!obs.contains(o)) {
  12. obs.addElement(o);
  13. }
  14. }
  15. public synchronized void deleteObserver(Observer o) {
  16. obs.removeElement(o);
  17. }
  18. public void notifyObservers() {
  19. notifyObservers(null);
  20. }
  21. public void notifyObservers(Object arg) {
  22. Object[] arrLocal;
  23. synchronized (this) {
  24. if (!changed)
  25. return;
  26. arrLocal = obs.toArray();
  27. clearChanged();
  28. }
  29. for (int i = arrLocal.length-1; i>=0; i--)
  30. ((Observer)arrLocal[i]).update(this, arg);
  31. }
  32. public synchronized void deleteObservers() {
  33. obs.removeAllElements();
  34. }
  35. protected synchronized void setChanged() {
  36. changed = true;
  37. }
  38. protected synchronized void clearChanged() {
  39. changed = false;
  40. }
  41. public synchronized boolean hasChanged() {
  42. return changed;
  43. }
  44. public synchronized int countObservers() {
  45. return obs.size();
  46. }
  47. }

Observable 的作用和地位等价于我们前面讲过Subject
Observable 是类,不是接口,类中已经实现了核心的方法,即管理Observer 的方法add.. delete .. notify…
Observer 的作用和地位等价于我们前面讲过的Observer, 有update

  1. package java.util;
  2. public interface Observer {
  3. void update(Observable o, Object arg);
  4. }

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

优点:

1、当两个对象之间送耦合,他们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者之间送耦合。主题所知道只是一个具体的观察者列表,每一个具体观察者都符合一个抽象观察者的接口。主题并不认识任何一个具体的观察者,它只知道他们都有一个共同的接口。

2、观察者模式支持“广播通信”。主题会向所有的观察者发出通知。

3、观察者模式符合“开闭原则”的要求。

缺点:

1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。

2、 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进 行循环调用,可能导致系统崩溃。

3、 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

观察者模式的适用场所

​ 1、一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。

2、一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。

3、一个对象必须通知其他对象,而并不知道这些对象是谁。需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

总结

1、观察者模式定义了对象之间的一对多关系。多个观察者监听同一个被观察者,当该被观察者的状态发生改变时,会通知所有的观察者。

2、观察者模式中包含四个角色。主题,它指被观察的对象。具体主题是主题子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;观察者,将对观察主题的改变做出反应;具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致。

3、主题用一个共同的接口来更新观察者。

4、观察者与被观察者之间用松耦合方式结合。

5、有多个观察者时,不可以依赖特定的通知次序。

6、使用观察者模式,可以从被观察者处推或者拉数据。

发表评论

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

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

相关阅读

    相关 设计模式观察模式

    观察者模式 什么是观察者模式 观察者模式属于行为模式的一种,定义了对象的通用交流方式。 观察者模式定义了一对多的关系,一个对象改变了状态,则其它所有依赖

    相关 设计模式观察模式

    前言 使用场景: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟

    相关 设计模式观察模式

    当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。 即目标发生更

    相关 设计模式观察模式

    定义 当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对象状态。观察者模式提供给关联对象一种同步通信的手段,使某个对象与依赖它的其他对象之间保持状态同步

    相关 设计模式观察模式

     今天放假,又有时间继续啃《java设计模式》这本书了。每次学会一种设计模式内心都会有一种小小的成就感,但是懂是懂了,不知道会不会用。主要是现在没有什么项目经验,设计模式学了也

    相关 设计模式观察模式

    [上一篇:设计模式之策略模式][Link 1] 故事要从气象站说起,气象站有个WeatherData对象,这个对象负责拿到所有的气象数据(温度、湿度、气压),而气象站同时也

    相关 设计模式观察模式

    1 观察者模式 当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都

    相关 设计模式——观察模式

    > 设计模式: > > 前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定。而是一套用来提高代码可复用性、可维护性、可读性、稳健性、以及安全性的解决方案