其他分享
首页 > 其他分享> > 设计模式3 观察者模式

设计模式3 观察者模式

作者:互联网

定义

观察者模式(Observer Pattern)定义了一种一对多的依赖关系,这样一来,当一个对象改变状态时,它的所有依赖都会受到通知并自动更新。
——《Head FIRST 设计模式》

观察者模式是JDK中使用最多的模式之一。
AKA:发布-订阅模式(Publish/Subscribe)

主要角色

如下图(图源:程序员小灰):
观察者模式

举例

源自《Head FIRST设计模式》实现气象站。
气象站提供有温度、湿度、气压等气象数据,如果数据发生了更新,需要随时跟踪获取这些数据,并且display出来。

«interface» Subject +registerObserver() +removeObserver() +notifyObservers() WeatherData +registerObserver() +removeObserver() +notifyObservers() +getTemperature() +getHumidity() +getPressure() +measurementsChanged() «interface» Observer +update() CurrentConditionsDisplay +update() +display() «interface» DisplayElement +display()

首先建立接口

//被观察者接口
public interface Subject {
    void addObserver(Observer observer);
    void removerObserver(Observer observer);
    void notifyAllObservers();
}
public interface Observer {
    void update(float temp, float humidity);
}
public interface DisplayElement {
    void display();
}

实现被观察的WeatherData

public class WeatherData implements Subject{
    private float temperature;
    private float humidity;
    private List<Observer> observerList;
    public WeatherData() {
        observerList = new ArrayList<Observer>();
    }
    public void changeWeatherData(float temp,float humidity){
        this.temperature = temp;
        this.humidity = humidity;
        notifyAllObservers();
    }
    @Override
    public void addObserver(Observer observer) {
        observerList.add(observer);
    }
    @Override
    public void removerObserver(Observer observer) {
        int index = observerList.indexOf(observer);
        if(index >= 0)
            observerList.remove(index);
    }
    @Override
    public void notifyAllObservers() {
        for(Observer obs: observerList){
            obs.update(temperature,humidity);
        }
    }
}

实现天气布告板,他作为观察者观察者WeatherData

public class CurrentConditionsDisplay implements Observer,DisplayElement{
    private float temperature;
    private float humidity;
    private String displayName;
    //此处保留了对Subject的引用
    private Subject weatherData;

    public CurrentConditionsDisplay(String displayName, Subject weatherData) {
        this.displayName = displayName;
        this.weatherData = weatherData;
        weatherData.addObserver(this);
    }
    @Override
    public void update(float temp, float humidity) {
        this.temperature = temp;
        this.humidity = humidity;
        System.out.println(displayName + "气象布告栏:当前气温" + temperature + "℃,湿度:" + humidity + "%");
    }
  //Getter&Setter忽略
}

这里保留了对Subject的引用,在实践中是可以有所变化的。
这里的subject接口可以更改为抽象类,在类中定义一些通用的方法。实际上,Java内置的观察者模式就是需要继承自一个抽象类。下面会详细介绍Java内置的观察者模式。

推拉模型下的观察者模式

在上面这种代码运行下,当weather变化时,会将变化通知给观察者。也就是主题对象向观察者推送主题的详细信息,不管观察者是否需要。这种方式是推模型。
但是存在一些需求,观察者想去主动的拉取一些数据下来,而不是被动的被喂数据。先看代码

拉模型下的例子

还是这个例子,如果将逻辑改为拉模式,可以这样修改:
在WeatherData中,不再保留对Subject的引用,改为由参数传入
首先Observer修改接口参数,直接将主题对象(Subject)传入:

public interface Observer {
	//注意之前的参数为具体的温度湿度数值,现在是Subject
    void update(Subject subject);
}

那么在调用时,需要传入一个Subject的实例

public class WeatherData implements Subject{
    private float temperature;
    private float humidity;
    private List<Observer> observerList;
    public WeatherData() {
        observerList = new ArrayList<Observer>();
    }
    public void changeWeatherData(float temp,float humidity){
        this.temperature = temp;
        this.humidity = humidity;
        notifyAllObservers();
    }
    @Override
    public void addObserver(Observer observer) {
        observerList.add(observer);
    }
    @Override
    public void removerObserver(Observer observer) {
        int index = observerList.indexOf(observer);
        if(index >= 0)
            observerList.remove(index);
    }
    @Override
    public void notifyAllObservers() {
        for(Observer obs: observerList){
            obs.update(this);
        }
    }
}

实现void update(Subject)

public class CurrentConditionsDisplay implements Observer,DisplayElement{
    private float temperature;
    private float humidity;
    private String displayName;
    //此处保留了对Subject的引用
    private Subject weatherData;

    public CurrentConditionsDisplay(String displayName, Subject weatherData) {
        this.displayName = displayName;
        this.weatherData = weatherData;
        weatherData.addObserver(this);
    }
    @Override
    public void update(Subject subject) {
        this.temperature = ((WeatherData)subject).temperature;
        this.humidity = ((WeatherData)subject).humidity;
        System.out.println(displayName + "气象布告栏:当前气温" + temperature + "℃,湿度:" + humidity + "%");
    }
  //Getter&Setter忽略
}

推拉模型对比

部分参考Java设计模式の观察者模式(推拉模型)

  1. 推模型是假定主题对象知道观察者需要的数据;而拉模型是主题对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需要取值。
  2. 推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,以后随着业务的变化这个方法可能不再适用;拉模型update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要,但是需要将主题对象完全暴露给使用者。

Java内置观察者模式

Java本身有自带实现观察者模式。在Java语言的java.util库里面,提供了一个Observable类以及一个Observer接口。

Observer接口

JDK8文档Interface Observer
JDK9已弃用Interface Observer
这个接口只定义了一个方法,即update()方法,当被观察者对象的状态发生变化时,被观察者对象的notifyObservers()方法就会调用这一方法。

package java.util;

/**
 * A class can implement the <code>Observer</code> interface when it
 * wants to be informed of changes in observable objects.
 * 当类希望被告知可观察对象的变化时,它可以实现<code>Observer</code>接口
 * 
 * @author  Chris Warth
 * @see     java.util.Observable
 * @since   JDK1.0
 */
public interface Observer {

     *
     * @param   o     the observable object 被观察/主题对象
     * @param   arg   an argument passed to the <code>notifyObservers</code> method.传递给notifyObservers的参数          
     */
    void update(Observable o, Object arg);
}

Observable类

JDK8文档Class Observable
JDK9已弃用Class Observable
被观察者类都是java.util.Observable类的子类。
核心函数有两个:

package java.util;

/**
 * This class represents an observable object, or "data"
 * in the model-view paradigm. It can be subclassed to represent an
 * object that the application wants to have observed.
 * <p>
 * An observable object can have one or more observers. An observer
 * may be any object that implements interface <tt>Observer</tt>. After an
 * observable instance changes, an application calling the
 * <code>Observable</code>'s <code>notifyObservers</code> method
 * causes all of its observers to be notified of the change by a call
 * to their <code>update</code> method.
 * <p>
 * The order in which notifications will be delivered is unspecified.
 * The default implementation provided in the Observable class will
 * notify Observers in the order in which they registered interest, but
 * subclasses may change this order, use no guaranteed order, deliver
 * notifications on separate threads, or may guarantee that their
 * subclass follows this order, as they choose.
 * <p>
 * Note that this notification mechanism has nothing to do with threads
 * and is completely separate from the <tt>wait</tt> and <tt>notify</tt>
 * mechanism of class <tt>Object</tt>.
 * <p>
 * When an observable object is newly created, its set of observers is
 * empty. Two observers are considered the same if and only if the
 * <tt>equals</tt> method returns true for them.
 */
public class Observable {
    private boolean changed = false;
    //Vector线程安全
    private Vector<Observer> obs;

    /** Construct an Observable with zero Observers. */

    public Observable() {
        obs = new Vector<>();
    }

    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }
	
    public void notifyObservers() {
        notifyObservers(null);
    }
	//通知观察者
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;
		//如果changed标记是true,就把所有观察者放进一个数组,并把changed标记置为false
        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }
		//通知所有数组里的观察者去update信息
        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

	//这里是protected,
    protected synchronized void setChanged() {
        changed = true;
    }

    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }

    public synchronized int countObservers() {
        return obs.size();
    }
}

在使用时,一个简单的例子实现被观察者

public class Watched extends Observable {
    private String state = "";

    public String getState() {
        return state;
    }
	//在setter里面通知所有观察者,通过setChanged()函数和notifyObservers()函数
    public void setState(String state) {
        if (!this.state.equals(state)) {
            this.state = state;
            setChanged();
        }
        notifyObservers();
    }
}

缺点

Observable是一个类,而Java又不支持多继承。
在JDK9中已弃用,原因是支持Observer和支持的事件模型Observable 非常有限,发送通知的顺序 Observable不明确,状态变化与通知不是一一对应的。

参考

Java设计模式の观察者模式(推拉模型)
观察者设计模式(二) - 推拉模式
漫画:什么是“观察者模式”?
简说设计模式——观察者模式

标签:Observer,void,观察者,模式,humidity,设计模式,public,Subject
来源: https://blog.csdn.net/mxb1234567/article/details/120733766