1、天气预报项目需求

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

1)气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)

2)需要设计开放型 API,便于其他第三方也能接入气象站获取数据

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

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

2、天气预报设计方案 1-普通方案

2.1 思路

WeatherData 类

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

3chhz

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

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

2.2 示意图

3lykz
CurrentConditions(当前的天气情况)
可以理解成使我们气象局的网站 // 推送

2.3 代码实现

CurrentConditions 当前天气情况

package com.wenze.design.observer.tradition;  
  
/**  
 * 当前的天气情况  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-11-06 17:33:27  
 * @since 1.0  
 */
public class CurrentConditions {  
    private float temperature;  
    private float pressure;  
    private float humidity;  
    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 mTemperature: " + temperature + "***");  
        System.out.println("***Today mPressure: " + pressure + "***");  
        System.out.println("***Today mHumidity: " + humidity + "***");  
    }  
}

WeatherData 天气数据类

package com.wenze.design.observer.tradition;  
  
import lombok.Getter;  
  
/**  
 * 天气数据  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-11-06 17:39:48  
 * @since 1.0  
 */
@Getter  
public class WeatherData {  
    private float temperature;  
    private float pressure;  
    private float humidity;  
    private CurrentConditions currentConditions;  
    public WeatherData(CurrentConditions currentConditions) {  
        this.currentConditions = currentConditions;  
    }  
    public void dataChange() {  
        currentConditions.update(getTemperature(), getPressure(), getHumidity());  
    }  
    public void setData(float temperature, float pressure, float humidity) {  
        this.temperature = temperature;  
        this.pressure = pressure;  
        this.humidity = humidity;  
        dataChange();  
    }  
}

Client 客户端

package com.wenze.design.observer.tradition;  
  
/**  
 * 客户端  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-11-06 17:43:03  
 * @since 1.0  
 */
public class Client {  
    public static void main(String[] args) {  
        // 创建接入方 currentConditions        
        CurrentConditions currentConditions = new CurrentConditions();  
        // 创建WeatherData并将接入方currentConditions传递到WeatherData中  
        WeatherData weatherData = new WeatherData(currentConditions);  
        // 更新天气情况  
        weatherData.setData(30, 150, 40);  
        // 天气情况变化  
        System.out.println("===============天气情况变化==============");  
        weatherData.setData(40, 160, 20);  
    }  
}

2.4 问题分析

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

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

3)违反 OCP 原则 => 观察者模式

在 WeatherData 中,当添加一个第三方,都需要创建一个对应的第三方的公告板对象,并加入到 dataChange,不利于维护,也不是动态加入

public void dataChange() {
	currentConditions.update(getTemperature(), getPressure(), getHumidity());
}

3、观察者模式

3.1 基本原理

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

1)奶站/气象局:Subject

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

Subject: 登记注册、移除和通知

1)registerObserver 注册

2)removeObserver 移除

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

Observer:接收输入

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

3.2 应用实例

1)思路分析图解

imcnh

2)代码实现

Observer

package com.wenze.design.observer.improve;  
  
/**  
 * 观察者接口,有观察者实现  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-11-06 18:12:35  
 * @since 1.0  
 */
public interface Observer {  
    void update(float temperature, float pressure, float humidity);  
}

CurrentConditions

package com.wenze.design.observer.improve;  
  
/**  
 * 当前天气情况  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-11-06 18:14:49  
 * @since 1.0  
 */
public class CurrentConditions implements Observer {  
    private float temperature;  
    private float pressure;  
    private float humidity;  
    @Override  
    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 mTemperature: " + temperature + "***");  
        System.out.println("***Today mPressure: " + pressure + "***");  
        System.out.println("***Today mHumidity: " + humidity + "***");  
    }  
}

Subject

package com.wenze.design.observer.improve;  
  
/**  
 * 接口,让 weatherData 实现  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-11-06 18:12:21  
 * @since 1.0  
 */
public interface Subject {  
    void registryObserver(Observer observer);  
    void removeObserver(Observer observer);  
    void notifyObservers();  
}

WeatherData

package com.wenze.design.observer.improve;  
  
import lombok.Getter;  
  
import java.util.ArrayList;  
import java.util.List;  
  
/**  
 * 天气数据  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-11-06 18:15:31  
 * @since 1.0  
 */
@Getter  
public class WeatherData implements Subject {  
    private float temperature;  
    private float pressure;  
    private float humidity;  
    private List<Observer> observers;  
    public WeatherData() {  
        observers = new ArrayList<>();  
    }  
    public void dataChange() {  
        notifyObservers();  
    }  
    public void setData(float temperature, float pressure, float humidity) {  
        this.temperature = temperature;  
        this.pressure = pressure;  
        this.humidity = humidity;  
        dataChange();  
    }  
    @Override  
    public void registryObserver(final Observer observer) {  
        observers.add(observer);  
    }  
    @Override  
    public void removeObserver(final Observer observer) {  
        observers.remove(observer);  
    }  
    @Override  
    public void notifyObservers() {  
        for (final Observer observer : observers) {  
            observer.update(getTemperature(), getPressure(), getHumidity());  
        }  
    }  
}

Client

package com.wenze.design.observer.improve;  
  
/**  
 * 客户端  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-11-06 18:20:43  
 * @since 1.0  
 */
public class Client {  
    public static void main(String[] args) {  
        // 创建一个 WeatherData        
        WeatherData weatherData = new WeatherData();  
        // 创建观察者  
        CurrentConditions currentConditions = new CurrentConditions();  
        // 注册到 weatherData   
        weatherData.registryObserver(currentConditions);  
        // 测试  
        System.out.println("通知各个注册的观察者,信息更新了");  
        weatherData.setData(10f, 100f, 30.3f);  
    }  
}

3.3 观察者模式的好处

1)观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。

2)这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类 WeatherData 不会修改代码,遵守了 OCP 原则。

4、观察者模式在 JDK 应用的源码分析

1)JDK 的 Observable 类就使用了观察者模式

2)代码分析

xakj9

3)模式角色分析

  1. Observable 的作用和地位等价于我们前面讲过 Subject
  2. Observable 是类,不是接口,类中已经实现了核心的方法,即管理 Observer 的方法 add、delete、notify
  3. Observer 的作用和地位等价于前面的 Observer,有 update
  4. Observable 和 Observer 的使用方法和前面的一样,只是 Observable 是类,通过集成来实现观察者模式