观察者模式

发布时间 2023-10-05 22:37:06作者: 长名06

观察者模式

案例引入

要求

气象站案例要求

  • 1.气象站,可以将每天测量到的湿度,温度,气压等气象信息以公告的形式发布出去(发布到自己的网站或者第三方平台)。
  • 2.需要设计开发新的api,便于其他第三方也能接入气象站的数据。
  • 3.提供温度,气压,湿度的接口。
  • 4.策略的数据更新后,实时的通知第三方。

普通方案实现案例

通过对气象项目的分析,我们可以初步设计一个WeatherData类。有温度(temperature),湿度(humidity),气压(pressure)三个属性,还有dataChange方法。

  • 1.通过getXxx方法,可以让第三方接入,并得到相关信息。
  • 2.气象站定时的调用dataChange()去更新数据,在数据更新时,当第三方再次获取时,就能得到最新的数据,当然也可以推送数据,即定时的将数据,推送到目标网站上。
代码实现
/**
 * 1.核心类,包含最新的天气信息数据,可以推送给别人
 * 2.含有 private CurrentConditions currentConditions;
 * 3.数据更新时,主动调用currentConditions进行数据推送,这样接入方,就会及时看到最新数据
 *
 * @author 长名06
 * @version 1.0
 */
public class WeatherData {

    //温度
    private float temperature;
    //气压
    private float pressure;
    //湿度
    private float humidity;

    private CurrentConditions currentConditions;

    public WeatherData(CurrentConditions c) {
        this.currentConditions = c;
    }

    public float getTemperature() {
        return temperature;
    }

    public float getPressure() {
        return pressure;
    }

    public float getHumidity() {
        return humidity;
    }

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

    //当数据有更新时,就调用这个setData
    public void setData(float temperature, float pressure, float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        //信息改变时,将最新信息,推送给接入方
        dataChange();
    }
}
/**
 * 显示今天天气情况,气象站自己的网站
 * @author 长名06
 * @version 1.0
 */
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();
    }

    //展示数据
    public void display(){
        System.out.println("今天温度" + temperature);
        System.out.println("今天气压" + pressure);
        System.out.println("今天湿度" + humidity);
    }
}
/**
 * 观察者模式
 * @author 长名06
 * @version 1.0
 */
public class Client {
    public static void main(String[] args) {
        CurrentConditions currentConditions = new CurrentConditions();
        WeatherData weatherData = new WeatherData(currentConditions);


        weatherData.setData(30,100,25);
        System.out.println("=====天气变化=====");
        weatherData.setData(35,100,30);
    }
}
问题分析
  • 1.无法在运行时,动态的添加第三方。
  • 2.在WeatherData中,当增加一个第三方,都需要创建一个对应的第三方的公告板对象,并加入到dataChange,不利于维护,也不是动态加入。
  • 3.违反了ocp(开闭原则),=> 使用观察者模式。

观察者模式原理

观察者模式,类似订牛奶的业务,在观察者模式下,有以下角色,奶站/气象局(Subject),用户/第三方网站(Observer),Subject中有登记注册,移除和通知接口方法,register()注册,remove()移除,notify()通知注册用户,根据不同需求,不同实现,可以通知用户取数据,也可以推送给用户数据,也可以更新数据,Observer中接收输入,也就是修改数据的方法update()。
观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer数据变化,类似定牛奶的业务,Subject是1,Observer是多。

观察者模式实现案例

uml图

代码实现
/**
 * @author 长名06
 * @version 1.0
 */
public interface Subject {

    public void register(Observer o);

    public void remove(Observer o);

    public void notifies();

}
public interface Observer {

    public void update(float temperature,float pressure,float humidity);

}
/**
 *  1.核心类,包含最新的天气信息数据,可以推送给别人
 *  2.含有 观察者集合。使用ArrayList管理
 *  3.数据更新时,主动调用观察者集合进行数据推送,这样接入方,就会及时看到最新数据
 * @author 长名06
 * @version 1.0
 */
public class WeatherData implements Subject{

    //温度
    private float temperature;
    //气压
    private float pressure;
    //湿度
    private float humidity;

    private List<Observer> observers = new ArrayList<>();

    public float getTemperature() {
        return temperature;
    }

    public float getPressure() {
        return pressure;
    }

    public float getHumidity() {
        return humidity;
    }

    public void dataChange(){
        notifies();//通知所有注册用户
    }

    //当数据有更新时,就调用这个setData
    public void setData(float temperature, float pressure, float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        //信息改变时,将最新信息,推送给接入方
        dataChange();
    }

    @Override
    public void register(Observer o) {
        observers.add(o);
    }

    @Override
    public void remove(Observer o) {
        if(observers.contains(o)) {
            observers.remove(o);
        }
    }

    @Override
    public void notifies() {
        for(Observer o : observers){
            o.update(this.temperature,this.pressure,this.humidity);
        }
    }
}
public class TenXun implements Observer{
    //温度
    private float temperature;
    //气压
    private float pressure;
    //湿度
    private float humidity;

    //更新数据 由WeatherData来调用,
    @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("====腾讯网站====");
        System.out.println("腾讯 今天温度" + temperature);
        System.out.println("腾讯 今天气压" + pressure);
        System.out.println("腾讯 今天湿度" + humidity);
    }
}
/**
 * @author 长名06
 * @version 1.0
 */
public class XingLang implements Observer{

    //温度
    private float temperature;
    //气压
    private float pressure;
    //湿度
    private float humidity;

    //更新数据 由WeatherData来调用,
    @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("====新浪网站====");
        System.out.println("新浪 今天温度" + temperature);
        System.out.println("新浪 今天气压" + pressure);
        System.out.println("新浪 今天湿度" + humidity);
    }
}
public class Baidu implements Observer {
    //温度
    private float temperature;
    //气压
    private float pressure;
    //湿度
    private float humidity;

    //更新数据 由WeatherData来调用,
    @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("====百度网站====");
        System.out.println("百度 今天温度" + temperature);
        System.out.println("百度 今天气压" + pressure);
        System.out.println("百度 今天湿度" + humidity);
    }

}
/**
 * @author 长名06
 * @version 1.0
 */
public class Client {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();

        Baidu baidu = new Baidu();
        TenXun tenXun = new TenXun();
        XingLang xingLang = new XingLang();

        weatherData.register(baidu);
        weatherData.register(tenXun);
        weatherData.register(xingLang);

        weatherData.setData(20,100,30.3F);

        weatherData.remove(xingLang);

        System.out.println("====某个用户不订阅了====");
        weatherData.setData(20,100,30.3F);
    }
}
观察者模式优势

1.使用观察者模式后,以集合的形式来管理用户(Observer),包括注册,移除和通知。
2.这样我们增加观察者(可以理解为一个新的用户),就不需要取修改核心类,WeatherData不用修改代码,遵守了ocp。

观察者模式在JDK源码分析

1.JDK的ObServable类就使用了观察者模式。


2.角色分析
​ 2.1 Observable的作用和地位等价于我们前面讲过的Subject.
​ 2.2 Observable是类,不是接口,类中也实现了核心的方法,即管理Observable的方法,add,remove,notify等。
​ 2.3 Observer的作用和地位等价于我们前面讲过的Observer,有update。
​ 2.4 Observable和Observer的使用方法和前面讲过的一样,只是Observable是类,通过继承来实现观察者模式。
只是为了记录自己的学习历程,且本人水平有限,不对之处,请指正。