设计模式(2)--观察者模式

什么是观察者模式

  举个例子来简单说明下这个模式:假如现在你在一家报社订阅了报纸,每当有新的期刊,那么他们就会把报纸送到你家,如果你什么时候不想看这一期刊的时候,你就可以取消订阅,那么这时候他们就不会将报纸送到你家了。这其实就是利用了观察者模式,先给出两个基本概念:主题,就相当于被观察的对象,这里指的就是报社;观察者,实时接收主题新的数据,这里指的就是订阅报纸的人。

深入理解观察者模式

  先给出一个问题,然后我们就根据这个问题进行讨论。

  问题:现在有个气象站,给一些气象显示器实时发送数据,气象显示器所显示的数据方式不一样,有些可能着重温度或者湿度,客户也可以根据自己的需求来开发一份气象显示器。

  根据这个问题,你的代码可能是这样的:

  以下代码不是一个文件

//TemperatureDisplay.java 
public class TemperatureDisplay {

    public void update(float temperature, float humidity, float pressure) {
        System.out.println("temperature is " + temperature);
    }
}

//HumidityDisplay.java 
public class HumidityDisplay {

    public void update(float temperature, float humidity, float pressure) {
        System.out.println("humidity is " + humidity);
    }
}

//MixDisplay.java 
public class MixDisplay {

    public void update(float temperature, float humidity, float pressure) {
        System.out.println("temperature is " + temperature);
        System.out.println("humidity is " + humidity);
        System.out.println("pressure is " + pressure);
    }
}

//WeatherData.java
public class WeatherData {

    private float temperature;

    private float humidity;

    private float pressure;

    private TemperatureDisplay temperatureDisplay;

    private HumidityDisplay humidityDisplay;

    private MixDisplay mixDisplay;

    public WeatherData() {
    }

    public WeatherData(TemperatureDisplay temperatureDisplay, HumidityDisplay humidityDisplay,
            MixDisplay mixDisplay) {
        super();
        this.temperatureDisplay = temperatureDisplay;
        this.humidityDisplay = humidityDisplay;
        this.mixDisplay = mixDisplay;
    }

    public float getTemperature() {
        return temperature;
    }

    public void setTemperature(float temperature) {
        this.temperature = temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public void setHumidity(float humidity) {
        this.humidity = humidity;
    }

    public float getPressure() {
        return pressure;
    }

    public void setPressure(float pressure) {
        this.pressure = pressure;
    }

        //当气象数据变更时调用      
    public void measurementsChange() {
        temperatureDisplay.update(temperature, humidity, pressure);
        humidityDisplay.update(temperature, humidity, pressure);
        mixDisplay.update(temperature, humidity, pressure);
    }
}

  这可能是你最初的想法,或许你的想法比这个更好

  我们来仔细看下上面的代码,你会发现这个代码的扩展性很差。如果有人想自定义一个气象显示器,那么你必须修改WeatherData这个类中的代码,显然这个代码是不可以让其他人随便进行修改的,因为这个是气象站提供的。所以气象站的设计人员肯定就需要重构这一代码。

 

  解决方案:

  我们发现这个气象站就好像是主题一样,而气象显示器就貌似是观察者。你们是怎么看的呢?

  看看气象显示器的代码,发现都只有一个update方法,当然你可以添加其他的方法,那么我们就可以定义一个接口Display,让所有的气象显示器都实现这个接口。然后在WeatherData类中添加一个集合属性,这个集合中包含的是Display这一类型的对象。

  为了可扩展性,可能有很多气象站,所以我们定义一个Subject的接口,该接口中主要有三个方法,分别是注册气象显示器、删除注册气象显示器、通知注册气象显示器,即registerObverse、removeObverse、notifyObverse。而每个气象显示器中增加一个都必须有一个Subject类型的属性,并且在构造方法中需要传递一个Subject类型的参数进行实例化这个对象,然后这个气象显示器在这个主题中进行注册。

  根据这个方案,示例代码如下:

//Subject.java
public interface Subject {

    void registerObverse();

    void removeObverse();

    void notifyObverse();
}

//Display.java
//这就是一个观察者类的接口
public interface Display {

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

//WeatherData.java
import java.util.ArrayList;
import java.util.List;

public class WeatherData implements Subject {

    private float temperature;

    private float humidity;

    private float pressure;

    private List<Display> displays;

    public WeatherData() {
        displays = new ArrayList<>();
    }

    public float getTemperature() {
        return temperature;
    }

    public void setTemperature(float temperature) {
        this.temperature = temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public void setHumidity(float humidity) {
        this.humidity = humidity;
    }

    public float getPressure() {
        return pressure;
    }

    public void setPressure(float pressure) {
        this.pressure = pressure;
    }

    @Override
    public void registerObverse(Display display) {
        displays.add(display);
    }

    @Override
    public void removeObverse(Display display) {
        int i = displays.indexOf(display);
        if(i>=0)
            displays.remove(display);
    }

    @Override
    public void notifyObverse() {
        for(Display display:displays){
            display.update(temperature, humidity, pressure);
        }
    }

    //当气象数据变化时就修改调用这一方法
    public void measurementsChanged(){
        notifyObverse();
    }
    
}

//MixDisplay.java
//这里写了一个气象显示器,其他的跟着类似
public class MixDisplay implements Display {

    private Subject subject;

    public MixDisplay(Subject subject) {
        this.subject = subject;
        subject.registerObverse(this);
    }

    public void remove() {
        subject.removeObverse(this);
    }

    @Override
    public void update(float temperature, float humidity, float pressure) {
        System.out.println("temperature is " + temperature);
        System.out.println("humidity is " + humidity);
        System.out.println("pressure is " + pressure);
    }

}

  这时候如果你想自定义一个显示器,只需要实现Display这一接口就可以了,然后在气象显示器中定义一个Subject类型的属性,然后在构造是进行注册该观察者,那么只要气象数据变化了你就可以接受到。这是不是具有很好的可扩展性呢?

  其实呢Java中内置中就包含了这一模式,其主题就是Observable(这是一个类),观察者则是Obverse(这是一个接口)。

  所以你可以试试用内置的这一模式实现下以上案例。

  如果你对该博客有什么见解或疑问,可以留言哦>v<

posted @ 2017-04-24 17:21  逆倒尘光  阅读(261)  评论(0编辑  收藏  举报