Java设计模式之观察者模式的两种实现

     观察者模式就是定义对象之间的一对多依赖,这样一来,当一个对象状态发生改变时,它的所有依赖者都会收到通知并自动更新。  这样的好处就是两个或多个对象之间松耦合,它们依然可以交互,但不太清楚彼此的细节。观察者模式提供了一种对象的设计,让主题和观察者之间松耦合。松耦合的设计可以让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。

      现在我们用一个简单的案例来熟悉观察者模式是怎么实现的。我们设计一个气象站,气象站提供天气数据,天气数据更新后,要实时显示在公告板上。

我们分别用自定义的观察者模式和Java内置的观察者模式两种方式来实现。首先是第一种。

定义主题接口,所有的主题都实现主题接口

package com.example.demo.observer.customize;

/**
 * 主题对象
 */
public interface Subject {

    /**
     * 添加观察者
     * @param observer
     */
    public void addObserver(Observer observer);

    /**
     * 删除指定观察者
     * @param observer
     */
    public void deleteObserver(Observer observer);

    /**
     * 通知所有观察者
     */
    public void notifyObservers();


}

实现我们的主题类,也就是我们的被观察者

    package com.example.demo.observer.customize;
    
    import java.util.ArrayList;
    
    /**
     * 被观察者对象,实现主题接口
     */
    public class WeatherData implements Subject {
    
         //温度
        private float temperature;
        //湿度
        private float humidity;
        //气压
        private float airpressure;

        //观察者列表
        private ArrayList observerArrayList;
    
        public WeatherData() {
            this.observerArrayList = new ArrayList();
        }
    
        /**
         * 添加指定观察者对象
         * @param observer
         */
        @Override
        public void addObserver(Observer observer) {
            this.observerArrayList.add(observer);
        }
    
        /**
         * 删除指定观察者对象
         * @param observer
         */
        @Override
        public void deleteObserver(Observer observer) {
            int i;
            if((i = observerArrayList.indexOf(observer)) != -1) {
                this.observerArrayList.remove(i);
            }
        }
    
        /**
         * 通知观察者
         */
        @Override
        public void notifyObservers() {
            for(Observer observer : this.observerArrayList) {
                observer.update(this.temperature, this.humidity, this.airpressure);
            }
        }
    
        /**
         * 被观察者数据发生改变
         * @param temperature
         * @param humidity
         * @param airpressure
         */
        public void setMeasurements(float temperature, float humidity, float airpressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.airpressure = airpressure;
            this.measurementsChanged();
        }
    
        /**
         * 修改后,通知观察者
         */
        public void measurementsChanged() {
            this.notifyObservers();
        }
    }

定义我们的观察者接口,所有的观察者都实现此接口,称为观察者对象

package com.example.demo.observer.customize;

/**
 * 观察者接口
 */
public interface Observer {

    /**
     * 调用观察者者更新接口
     * @param temperature
     * @param humidity
     * @param airpressure
     */
    public void update(float temperature, float humidity, float airpressure);
}

定义一个公告板显示接口,观察者要实现此接口来显示在公告板上

package com.example.demo.observer.customize;

public interface DisplayElement {

    public void display();
}

定义观察者对象:观察者对象,在初始化构造的时候,注册主题(成为观察主题的对象)

package com.example.demo.observer.customize;

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    private float airpressure;
    private Subject subject;

    public CurrentConditionsDisplay(Subject subject) {
        this.subject = subject;
        this.subject.addObserver(this);
    }

    @Override
    public void update(float temperature, float humidity, float airpressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.airpressure = airpressure;
        this.display();
    }

    @Override
    public void display() {
        System.out.println("气温:"+ this.temperature+"\t"+"湿度:"+this.humidity+"\t"+"气压:"+this.airpressure);
    }
}
package com.example.demo.observer.customize;

public class ForecastDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    private float airpressure;
    private Subject subject;

    public ForecastDisplay(Subject subject) {
        this.subject = subject;
        this.subject.addObserver(this);
    }

    @Override
    public void update(float temperature, float humidity, float airpressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.airpressure = airpressure;
        this.display();
    }

    @Override
    public void display() {
        System.out.println("气温:"+ this.temperature+"\t"+"湿度:"+this.humidity+"\t"+"气压:"+this.airpressure);
    }
}
package com.example.demo.observer.customize;

public class StatisticsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    private float airpressure;
    private Subject subject;

    public StatisticsDisplay(Subject subject) {
        this.subject = subject;
        this.subject.addObserver(this);
    }

    @Override
    public void update(float temperature, float humidity, float airpressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.airpressure = airpressure;
        this.display();
    }

    @Override
    public void display() {
        System.out.println("气温:"+ this.temperature+"\t"+"湿度:"+this.humidity+"\t"+"气压:"+this.airpressure);
    }
}

测试我们的观察者模式

package com.example.demo.observer.customize;

public class WeatherMain {

    public static void main(String[] args) {

        //被观察者
        WeatherData weatherData = new WeatherData();

        //观察者对象
        CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData);
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);

        weatherData.setMeasurements(80, 85, 90);

    }
}

第二种实现方式,用Java的内置的API实现观察者模式,主题对象继承Observable类,观察者对象实现Observer接口即可和以上方法类似。修改我们的主题对象。

package com.example.demo.observer.internal;

import java.util.Observable;

public class WeatherData extends Observable {

    private float temperature;
    private float humidity;
    private float airpressure;

    public WeatherData() {
    }

    /**
     * 被观察者数据发生改变
     * @param temperature
     * @param humidity
     * @param airpressure
     */
    public void setMeasurements(float temperature, float humidity, float airpressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.airpressure = airpressure;
        this.measurementsChanged();
    }

    /**
     * 修改后,通知观察者
     */
    public void measurementsChanged() {
        super.setChanged();
        super.notifyObservers();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getAirpressure() {
        return airpressure;
    }

}

Observable可以主动推(push)数据给观察者对象,也可以让观察者对象拉(pull)数据。

package com.example.demo.observer.internal;

import com.example.demo.observer.customize.DisplayElement;

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

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    private float airpressure;
    private Observable observable;

    public CurrentConditionsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof WeatherData) {
            this.temperature = ((WeatherData) o).getTemperature();
            this.humidity = ((WeatherData) o).getHumidity();
            this.airpressure = ((WeatherData) o).getAirpressure();
            this.display();
        }
    }

    @Override
    public void display() {
        System.out.println("气温:"+ this.temperature+"\t"+"湿度:"+this.humidity+"\t"+"气压:"+this.airpressure);
    }
}
package com.example.demo.observer.internal;

import com.example.demo.observer.customize.DisplayElement;

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

public class ForecastDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    private float airpressure;
    private Observable observeable;

    public ForecastDisplay(Observable observeable) {
        this.observeable = observeable;
        this.observeable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof WeatherData) {
            this.temperature = ((WeatherData) o).getTemperature();
            this.humidity = ((WeatherData) o).getHumidity();
            this.airpressure = ((WeatherData) o).getAirpressure();
            this.display();
        }
    }

    @Override
    public void display() {
        System.out.println("气温:"+ this.temperature+"\t"+"湿度:"+this.humidity+"\t"+"气压:"+this.airpressure);
    }

}
package com.example.demo.observer.internal;

import com.example.demo.observer.customize.DisplayElement;

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

public class StatisticsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    private float airpressure;
    private Observable observable;

    public StatisticsDisplay(Observable observable) {
        this.observable = observable;
        this.observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof WeatherData) {
            this.temperature = ((WeatherData) o).getTemperature();
            this.humidity = ((WeatherData) o).getHumidity();
            this.airpressure = ((WeatherData) o).getAirpressure();
            this.display();
        }
    }

    @Override
    public void display() {
        System.out.println("气温:"+ this.temperature+"\t"+"湿度:"+this.humidity+"\t"+"气压:"+this.airpressure);
    }
}

测试方法同上。  和自定义观察者模式的区别在于,通知观察者前首先要调用setChanged()方法,修改主题对象的标识,该标识的好处是可以自由的控制主题对象和观察者之间的交互,比如一些微小的数据修改不通知观察者,就可以通过该方法来控制;还有观察者update(Observable o, Object arg)接口的第一个参数Observable o是可以让观察者知道是哪个主题调用它,也可以通过该主题对象,去主题拉取数据。Object arg参数是主题对象推送过来的数据对象,观察者直接可以获取主题对象发送的数据进行处理。

经过测试我们可以观察到,通过Java内置的观察者模式,我们发现Observalbe的notifyObservers()方法通知观察者的时候和我们自定义观察者模式的顺序是不一样的,但如果我们的代码依赖这样的次序,这种实现就是错的。通知次序的改变,很可能会产生错误的结果。

另一方面,Observable是一个类,并不是一个接口,它限制了我们的复用,也违反了我们的OO设计原则(针对接口编程,而非针对实现编程)。如果主题对象要想继承其他超类,就会陷入两难的境地,毕竟Java不支持多继承,限制了Observable的复用潜力。再者,因为没有Observable接口,所以也无法建立自己的实现,和Java内置Observer API搭配使用。而且也违反了我们的设计原则:多用组合,少用继承。

所以不管用哪种方法都可以实现观察者模式,前提是根据我们的业务场景来。

 

你可能感兴趣的:(Java设计模式之观察者模式的两种实现)