23种设计模式之观察者模式

242 阅读4分钟

「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

我们这里由一个简单的项目需求引出观察者模式:

天气预报项目需求,具体要求如下:

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

  • 提供温度、气压和湿度的接口

  • 测量数据更新时,要能实时的通知给第三方

通过对气象站项目的分析,我们可以初步设计出一个WeatherData类

image.png

说明:

1)通过getXxx方法,可以让第三方接入,并得到相关信息

2)当数据有更新时,气象站通过调用dataChange()去更新数据,当第三方再次获取时,就能得到最新数据,当然也可以推送。

代码实现:

// 负责提供数据
public class WeatherDate {

    private float temperature;
    private float pressure;
    private float humidity;
    private CurrentConditions currentConditions;

    public WeatherDate(CurrentConditions currentConditions) {
        this.currentConditions = currentConditions;
    }
    
    public float getTemperature() {
        return temperature;
    }
    
    public float getPressure() {
        return pressure;
    }

    public float getHumidity() {
        return humidity;
    }
    
	//在WeatherData中,当增加一个第三方,都需要创建一个对应的第三方的公告板对象,并加入到dataChange,不利于维护,也不是动态加入
    public void DataChange(){
        // 调用接入方
        currentConditions.update(getTemperature(),getPressure(),getHumidity());
    }

    public void setDate(float temperature,float pressure,float humidity){
        this.temperature=temperature;
        this.pressure=pressure;
        this.humidity=humidity;
        DataChange();
    }
}


// 显示当前天气情况 类似气象站
public class CurrentConditions {

    private float temperature;
    private float pressure;
    private float humidity;

    // 跟新天气情况,由weatherData来进行调用 推送模式
    public void update(float temperature, float pressure, float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        display();
    }
    // 显示数据
    private void display() {
        System.out.println("Today temperature is "+temperature);
        System.out.println("Today pressure is "+pressure);
        System.out.println("Today humidity is "+humidity);
    }
}


普通方案——问题分析

1)其他第三方接入气象站获取数据的问题

2)无法在运行时动态的添加第三方(新浪网站)

3)违反ocp原则

观察者模式原理

观察者模式类似订牛奶业务

1)奶站/气象局:Subject

2)用户/第三方网站:Observer

Subject 登记注册、移除和通知

Observer 接收输入

  1. registerObserver()注册
  2. removeObserver()移除
  3. notifyObservers()通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定

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

UMI类图

image-20211116204249463

改进方案代码实现:

Subject被依赖端

public interface Subject {

    public void remove(Object o);
    public void notifyAllObservers();
    public void registry(Object o);
}

// 负责提供数据
public class WeatherDate implements Subject {

    private float temperature;
    private float pressure;
    private float humidity;
    private List<Observer> list;// 存放依赖对象

    public WeatherDate() {
        this.list=new ArrayList<Observer>();
    }

    public void DataChange(){
        // 调用接入方
        notifyAllObservers();
    }

    @Override
    public void remove(Object o) {
        if(list.contains(o)) list.remove(o);
        else System.out.println("删除服务失败!");
    }

    @Override
    public void notifyAllObservers() {

        for (Observer observer : list) {
            observer.update(this.temperature,this.pressure,this.humidity);
        }
    }

    @Override
    public void registry(Object o) {
        if(o instanceof Observer) {
            Observer oo=(Observer) o;
            list.add(oo);
        }else{
            System.out.println("添加失败!");
        }

    }

    public void setDate(float temperature, float pressure, float humidity){
        this.temperature=temperature;
        this.pressure=pressure;
        this.humidity=humidity;
        DataChange();
    }
}

Observe依赖的对象

public interface Observer {

     void update(float temperature, float pressure, float humidity);
     void display();
}


// 显示当前天气情况 类似气象站
public class CurrentConditions implements Observer{

    private float temperature;
    private float pressure;
    private float humidity;

    // 跟新天气情况,由weatherData来进行调用 推送模式
    public void update(float temperature, float pressure, float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        display();
    }
    // 显示数据
    public void display() {
        System.out.println("Today temperature is "+temperature);
        System.out.println("Today pressure is "+pressure);
        System.out.println("Today humidity is "+humidity);
    }
}

Client客户端

public class Client {
    public static void main(String[] args) {
        Observer o=new CurrentConditions();
       WeatherDate weatherDate=new WeatherDate();
       weatherDate.registry(o);// 注册
       weatherDate.setDate(36.8f,89,78);// 数据更新


    }
}

观察者模式在jdk中的应用

Subject端

image-20211116233723023

Observer依赖对象

image-20211116233824034

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

模式角色分析

  • observable 的作用和地位等价于我们前面讲过Subject

  • Observable是类,不是接口,类中已经实现了核心的方法,即管理Observer的方法add.. delete .. notify...

  • Observer的作用和地位等价于我们前面讲过的Observer,有update

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