Java设计模式之观察者模式

蔚落 2023-07-19 12:07 14阅读 0赞

代码应该如同晚霞中的莲花一样地关闭(免于改变),如同晨曦中的莲花一
样地开放(能够扩展)

定义

观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新,大致构成如下

在这里插入图片描述

总结:
在这里插入图片描述

应用场景

现在又如下需求:
天气气象局可以测量天气的温度、湿度和气压,当需要测量新的天气测量数据measurementsChanged() 方法就会被调用,现在有2个需要使用天气数据的通知板,如果天气数据有更新,需要马上通知这两个通知板,并且未来可能会有其他通知板加入进来

这时候你需要如何设计? 这就是典型的发布订阅场景,只需要通知板订阅天气气象局即可,如果某个通知板不想受到气象通知了,取消订阅即可

代码实现

首先定义个 Subject 接口,接口中有一些对观察者的一些操作具体看代码注释
Subject

  1. /** * @author WH * @version 1.0 * @date 2020/3/23 22:12 * @Description 主题接口 */
  2. public interface Subject {
  3. /** * 注册观察者 * @param o */
  4. void registerObserver(Observer o);
  5. /** * 删除观察者 * @param o */
  6. void removeObserver(Observer o);
  7. /** * 通知观察者 */
  8. void notifyObservers();
  9. }

WeatherData类,Subject 的具体实现
WeatherData

  1. /** * @author WH * @version 1.0 * @date 2020/3/23 22:23 * @Description TODO */
  2. public class WeatherData implements Subject {
  3. private List<Observer> observers;
  4. private float temperature;
  5. private float humidity;
  6. private float pressure;
  7. public WeatherData() {
  8. observers = new ArrayList();
  9. }
  10. @Override
  11. public void registerObserver(Observer o) {
  12. System.out.println("添加观察者");
  13. observers.add(o);
  14. }
  15. @Override
  16. public void removeObserver(Observer o) {
  17. int i = observers.indexOf(o);
  18. if (i >= 0) {
  19. observers.remove(o);
  20. }
  21. }
  22. @Override
  23. public void notifyObservers() {
  24. observers.forEach(s -> s.update(temperature, humidity, pressure));
  25. }
  26. /** * 通知给观察者 */
  27. public void measurementsChanged() {
  28. notifyObservers();
  29. }
  30. /** * 更新天气数据测试方法 * @param temperature 温度 * @param humidity 湿度 * @param pressure 气压 */
  31. public void setMeasurements(float temperature, float humidity, float pressure) {
  32. this.temperature = temperature;
  33. this.humidity = humidity;
  34. this.pressure = pressure;
  35. measurementsChanged();
  36. }
  37. }

这里再定义一个观察者接口Observer,用于观察者订阅消息的实现,即消息有update需要调用的方法
Observer

  1. /** * @author WH * @version 1.0 * @date 2020/3/23 22:14 * @Description 观察者接口 */
  2. public interface Observer {
  3. /** * 抽象观察者具体实现 * @param temp * @param humidity * @param pressure */
  4. void update(float temperature, float humidity, float pressure);
  5. }

为了统一控制通知板的通知消息,我们抽象出一个DisplayElement接口出来控制订阅办的打印消息
DisplayElement

  1. /** * @author WH * @version 1.0 * @date 2020/3/23 22:21 * @Description TODO */
  2. public interface DisplayElement {
  3. /** * 布告板需要显示调用次方法 */
  4. void display();
  5. }

ForecastDisplay
现在是创建具体的观察者,观察者都应该实现Observer和DisplayElement接口

  1. /** * @author WH * @version 1.0 * @date 2020/3/23 23:07 * @Description TODO */
  2. public class ForecastDisplay implements Observer, DisplayElement {
  3. private float temperature;
  4. private float pressure;
  5. private Subject weatherData;
  6. public ForecastDisplay(Subject weatherData) {
  7. this.weatherData = weatherData;
  8. weatherData.registerObserver(this);
  9. }
  10. @Override
  11. public void display() {
  12. System.out.println("Current conditions: " + temperature
  13. + "F degrees and " + pressure + "% pressure");
  14. }
  15. @Override
  16. public void update(float temperature, float humidity, float pressure) {
  17. this.temperature = temperature;
  18. this.pressure = pressure;
  19. display();
  20. }
  21. }
  22. /** * @author WH * @version 1.0 * @date 2020/3/23 22:49 * @Description 目前天气布告板 */
  23. public class CurrentConditionsDisplay implements Observer, DisplayElement {
  24. private float temperature;
  25. private float humidity;
  26. private Subject weatherData;
  27. /** * 注册为观察者 * @param weatherData */
  28. public CurrentConditionsDisplay(Subject weatherData) {
  29. this.weatherData = weatherData;
  30. weatherData.registerObserver(this);
  31. }
  32. /** * 显示最近温度湿度 */
  33. @Override
  34. public void display() {
  35. System.out.println("Current conditions: " + temperature
  36. + "F degrees and " + humidity + "% humidity");
  37. }
  38. /** * 当调用update,将温度和湿度保存下来 * @param temperature * @param humidity * @param pressure */
  39. @Override
  40. public void update(float temperature, float humidity, float pressure) {
  41. this.temperature = temperature;
  42. this.humidity = humidity;
  43. display();
  44. }
  45. }

测试

  1. /** * @author WH * @version 1.0 * @date 2020/3/23 22:54 * @Description TODO */
  2. public class Test {
  3. public static void main(String[] args) {
  4. WeatherData weatherData = new WeatherData();
  5. CurrentConditionsDisplay current = new CurrentConditionsDisplay(weatherData);
  6. ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
  7. weatherData.setMeasurements(80, 65, 30.4f);
  8. weatherData.setMeasurements(82, 70, 29.2f);
  9. weatherData.setMeasurements(78, 90, 29.2f);
  10. }
  11. }

在这里插入图片描述

如果我们需要添加新的通告版,只需要实现这两个接口,就可以自动通知了,如果我们其中有通告版不想受到消息了,移除自己即可,这就是观察者模式

Java内置的观察者模式

Java内置API支持实现观察者模式,其中观察者只需要实现内置的类Observer,之前的WeatherData也主需要继承内置Observable,用到了继承就知道以后的扩展肯定不会太好
大致改动如下
WeatherData

  1. public class WeatherData extends Observable {
  2. private float temperature;
  3. private float humidity;
  4. private float pressure;
  5. public float getTemperature() {
  6. return temperature;
  7. }
  8. public void setTemperature(float temperature) {
  9. this.temperature = temperature;
  10. }
  11. public float getHumidity() {
  12. return humidity;
  13. }
  14. public void setHumidity(float humidity) {
  15. this.humidity = humidity;
  16. }
  17. public float getPressure() {
  18. return pressure;
  19. }
  20. public void setPressure(float pressure) {
  21. this.pressure = pressure;
  22. }
  23. /** * 不需要手动维护观察者 * */
  24. public WeatherData() {
  25. }
  26. public void measurementsChanged() {
  27. setChanged();
  28. // 推送消息
  29. notifyObservers();
  30. }
  31. public void setMeasurements(float temperature, float humidity, float pressure) {
  32. this.temperature = temperature;
  33. this.humidity = humidity;
  34. this.pressure = pressure;
  35. measurementsChanged();
  36. }
  37. }

CurrentConditionsDisplay

  1. public class CurrentConditionsDisplay implements Observer, DisplayElement {
  2. private float temperature;
  3. private float humidity;
  4. private Observable observable;
  5. /** * 登记为观察者 * @param observable */
  6. public CurrentConditionsDisplay(Observable observable) {
  7. this.observable = observable;
  8. observable.addObserver(this);
  9. }
  10. @Override
  11. public void display() {
  12. System.out.println("Current conditions: " + temperature
  13. + "F degrees and " + humidity + "% humidity");
  14. }
  15. @Override
  16. public void update(Observable o, Object arg) {
  17. if (o instanceof WeatherData) {
  18. WeatherData weatherData = (WeatherData)o;
  19. this.temperature = weatherData.getTemperature();
  20. this.humidity = weatherData.getHumidity();
  21. display();
  22. }
  23. }
  24. }

发表评论

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

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

相关阅读

    相关 Java 设计模式观察模式

    一、了解观察者模式 1.1 什么是观察者模式 观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象状态改变时,它的所有依赖者都会收到通知并自动更新。 典型的问

    相关 JAVA设计模式观察模式

    1、初步认识 观察者模式的定义:   在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。 大白话:   其实就是