设计模式(二):观察者模式

设计模式(二):观察者模式

本文将会通过一个小Demo来记述使用观察者模式的好处以及Java中内置的观察者模式。


Demo描述:
气象站提供数据:温度、气压、和湿度。这时候有第三方公司需要接入今天的数据,来展示在公司的公告板上。气象站测量数据更新时需要实时更新通知给第三方。


通常的设计解决方案:采用OO原则:公报板CurrentCoditions的update()方法调用datachage()方法,然后通过display()方法去展示数据内容。(OO原则过于简单,不做详细描述

CurrentCoditions.java:(公告板)

public class CurrentCoditions {
    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("*****公告板:******");
        System.out.println("今日的温度:"+Temperature);
        System.out.println("今日的气压:"+Pressure);
        System.out.println("今日的湿度:"+Humidity);
    }
}

WeatherData.java:

public class WeatherData {
    private float Temperature;
    private float Pressure;
    private float Humidity;

    private CurrentCoditions currentCoditions;
    public WeatherData(CurrentCoditions currentCoditions){
       this.currentCoditions= currentCoditions;
    }

    public float getTemperature() {
        return Temperature;
    }


    public float getPressure() {
        return Pressure;
    }


    public float getHumidity() {
        return Humidity;
    }


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

    //假装气象站调用了这个信息,设置参数(模拟气象站有新的数据并通知)
    public void setData(float Temperature,float Pressure,float Humidity){
        this.Temperature=Temperature;
        this.Pressure=Pressure;
        this.Humidity=Humidity;
        dataChange();
    }
}

InternetWeather.java:(气象站)

public class InternetWeather {
    public static void main(String[] args){
        CurrentCoditions currentCoditions;//公告板
        WeatherData weatherData;

        currentCoditions=new CurrentCoditions();
        weatherData=new WeatherData(currentCoditions);

        weatherData.setData(30,150,40);

    }
}

运行结果:
设计模式(二):观察者模式_第1张图片
好了,明显我们已经达到要求了。
这时候。。。。


新需求来啦:当前这个公司的公报版做好了。另一个公司说我想做一个天气预报的API接口,然后我也做个预报下个礼拜的公报版。需要设计开放性API,便于第三方也能接入气象站。
设想用传统的OO原则去解决新需求会产生什么问题?
预报公告板,内容肯定和今日公告板不同。所以,如果设计一个预告公告板,岂不是要新加一个预告公告板类,然后在WeatherData中又去加入该类呢。如果还有其他不只一家公司呢。明显,OO原则不适用。

解决方案: 不能每次都修改weatherData.因为每加入一个公司,都要增加一个公告板,即构造函数都要增加参数,每次都需要改和重新编译。所以,分析当前的变化部分与不变化部分。公报版变化所以抽象为接口和实现。
接口函数weatherdata有了公告板也要不停的变化,所以也要抽象为接口和实现。
1):其他第三方公司接入气象站获取数据的问题
2):无法在运行时动态的添加第三方
看我们,如何用观察者模式巧妙的解决它!(代码分析在下方,先贴源码
Observer.java:【接口】(观察者)

package newSolution;

public interface Observer {
    public void update(float Temperature,float Pressure,float Humidity);
}

Subject.java:【接口】(实现注册、移除和通知观察者)

package newSolution;

public interface Subject {
    public void registerObserver(Observer observer);
    public void removeObserver(Observer observer);
    public void notifyObservers();
}

WeatherData.java:

package newSolution;

import java.util.ArrayList;

public class WeatherData implements Subject {
    private float Temperature;
    private float Pressure;
    private float Humidity;

    private ArrayList observers;

    public WeatherData(){
        observers=new ArrayList();
    }

    public float getTemperature() {
        return Temperature;
    }


    public float getPressure() {
        return Pressure;
    }


    public float getHumidity() {
        return Humidity;
    }


    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 registerObserver(Observer observer) {
            observers.add(observer);
    }

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

    @Override
    public void notifyObservers() {
        for(int i=0,len=observers.size();i

CurrentCoditions.java:(公告板)


package newSolution;

public class CurrentCoditions 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("*****公告板:******");
        System.out.println("今日的温度:"+Temperature);
        System.out.println("今日的气压:"+Pressure);
        System.out.println("今日的湿度:"+Humidity);
    }
}


ForcastConditions.java:(明日天气预告公告版)

package newSolution;

public class ForcastConditions 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("*****公告板:******");
        System.out.println("明日的温度:"+Temperature);
        System.out.println("明日的气压:"+Pressure);
        System.out.println("明日的湿度:"+Humidity);
    }
}

InternetWeather.java:(气象站)

package newSolution;

public class InternetWeather {
    public static void main(String[] args){
        CurrentCoditions currentCoditions;//公告板
        ForcastConditions forcastConditions;//明日公告板
        WeatherData weatherData;

        weatherData=new WeatherData();
        currentCoditions=new CurrentCoditions();
        forcastConditions=new ForcastConditions();

        //注册观察者
     weatherData.registerObserver(currentCoditions);
     weatherData.registerObserver(forcastConditions);

     //数据更新
     weatherData.setData(37,180,50);

     //取消今日公报版
     weatherData.removeObserver(currentCoditions);
     weatherData.setData(40,250,50);

    }
}

设计模式(二):观察者模式_第2张图片

代码分析:
首先,我们注册了观察者Observer,它有个数据更新方法,由今日公报版、明日天气预报公告板去实现update()方法,update()方法去调用2个类自己定义的display()方法去实现展示内容。然后就是Subject,它是来实现注册、移除和通知观察者的,它由WeatherData天气数据操控类来实现,在WeatherData中添加了ArrayList observers;来管理Observer观察者,且放入了构造函数中。在main()方法中,通过初始化2个公告版类,然后再初始化WeatherData并将他们2个注册到Subject中实现通知功能。以后,每当有不同的公司的公告板或者其他业务,只需要实现Observer观察者,然后通过WeatherData实现定制版功能。通过Observer这个接口,就可以轻松的对外开放。任何业务只要实现该接口就可以实现被托管。


观察者模式到底是啥?

  1. 观察者模式就像定牛奶业务
    1):牛奶配送商店:Subject
    2):顾客(被上门服务)(观察者):Observer
  2. Subject:登记注册、移除、通知
    interface Subject:
    registerObserver()
    removeObserver()
    notifyObserver()
  3. Observer:接收输入
    interface Observer:
    update()
  4. 观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,
    Subject通知Observer变化。

就像牛奶配送商店和顾客。顾客可以打电话给商店说:“以后每周给我送牛奶!”,商店于是把客户的地址和电话记录下来。突然,某天,客户说:“我不想喝牛奶了,不要给我送了~”,商店接收到通知后,就将该用户移除了商店的配送名单中。

可能代码分析那里有点绕,不过多回顾哈上方的源码,相信也是很好理解的!


扩展:Java中内置的观察者: 松耦合、高内聚、隔离。
其中,
松耦合(使用于对象之间):可以交互,但不存在依赖。(异步,不需要等待,不需要直到对象的具体实现)(比如subject和observer)。
高内聚(适用于对象之内)。

对于Java内置的观察者,需要了解:
ObserVable:相当于Subject.实行注册监听等。有一点不同的是。它是类,是需要继承它而不是接口。(实现了注册、移除和通知 功能),使用了继承,子类的注册移除和通知等功能就可以不用自己去实现了。
Observer:是接口,实现Update().(推通知和拉通知都是可以的)
setChange()方法一定要调用(为了通知的灵活性)

WeatherData.java:

package Java;

import java.util.Observable;

public class WeatherData extends Observable {
    private float Temperature;
    private float Pressure;
    private float Humidity;


    public WeatherData(){

    }

    public float getTemperature() {
        return Temperature;
    }


    public float getPressure() {
        return Pressure;
    }


    public float getHumidity() {
        return Humidity;
    }


    public void dataChange(){
        //java内置的观察者中前面一定要做设置变化
        this.setChanged();//源码中有个boolean变量(可以自己调整是否接收)
        this.notifyObservers(new Data(getTemperature(),getPressure(),getHumidity()));
    }

    //假装气象站调用了这个信息,设置参数(模拟气象站有新的数据并通知)
    public void setData(float Temperature,float Pressure,float Humidity){
        this.Temperature=Temperature;
        this.Pressure=Pressure;
        this.Humidity=Humidity;
        dataChange();
    }

    public class Data{//存放数据参数
        public float Temperature;
        public float Pressure;
        public float Humidity;
        public Data(float temperature,float pressure,float humidity){
            Temperature=temperature;
            Pressure=pressure;
            Humidity=humidity;
        }

    }
}

CurrentCoditions.java:

package Java;

import java.util.Observable;
import java.util.Observer;

public class CurrentCoditions implements Observer {
    private float Temperature;
    private float Pressure;
    private float Humidity;

    @Override
    public void update(Observable o, Object arg) {
        this.Temperature=((WeatherData.Data)arg).Temperature;
        this.Pressure=((WeatherData.Data)arg).Pressure;
        this.Humidity=((WeatherData.Data)arg).Humidity;
        display();
    }

    public void display(){
        System.out.println("*****公告板:******");
        System.out.println("今日的温度:"+Temperature);
        System.out.println("今日的气压:"+Pressure);
        System.out.println("今日的湿度:"+Humidity);
    }

}

ForcastConditions.java:

package Java;

import java.util.Observable;
import java.util.Observer;

public class ForcastConditions implements Observer {
    private float Temperature;
    private float Pressure;
    private float Humidity;



    @Override
    public void update(Observable o, Object arg) {
        this.Temperature=((WeatherData.Data)arg).Temperature;
        this.Pressure=((WeatherData.Data)arg).Pressure;
        this.Humidity=((WeatherData.Data)arg).Humidity;
        display();
    }

    public void display(){
        System.out.println("*****公告板:******");
        System.out.println("明日的温度:"+Temperature);
        System.out.println("明日的气压:"+Pressure);
        System.out.println("明日的湿度:"+Humidity);
    }
}

InternetWeather.java:

package Java;



public class InternetWeather {
    public static void main(String[] args){
        CurrentCoditions currentCoditions;//公告板
        ForcastConditions forcastConditions;//明日公告板
        WeatherData weatherData;

        weatherData=new WeatherData();
        currentCoditions=new CurrentCoditions();
        forcastConditions=new ForcastConditions();

        //注册观察者
        weatherData.addObserver(currentCoditions);
        weatherData.addObserver(forcastConditions);
        //注意顺序:通知顺序,后进先通知

        //数据更新
        weatherData.setData(30,150,40);

        //取消今日公报版
        weatherData.deleteObserver(currentCoditions);
        weatherData.setData(37,250,80);
    }
}

实现效果和自己定义接口一样。貌似Java中的内置观察者更加方便,但是Observable是继承,单继承自然有很多缺点。。。
设计模式(二):观察者模式_第3张图片


总结:
观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,
Subject通知Observer变化。

你可能感兴趣的:(设计模式)