观察者模式(使用JDK内置的工具)

定义:观察者模式(也称发布者/订阅者模式),提供了对象之间的一种依赖关系,当被依赖者发生改变时,它的所有依赖者都会收到通知,并作出反应。

jdk的util包提供了一个类Observable和一个接口Observer。

Observable(被观察者/发布者)

这个类代表一个可观察到的对象,或数据。它可以被细分为代表一个应用程序想要观察的对象。

一个可观察的对象可以有一个或多个观察者。一个观察者可能是任何实现观察者接口的对象。当一个被观察者发生变化后,应用程序可以调用notifyObservers方法,调用所有观察者的update方法让它们注意到这些变化。

被观察者使用了Vector容器,所以默认按照观察者的添加顺序保存,通知的时候按照相反顺序执行。但是,子类可以改变这些顺序,所以,顺序不保证。

被观察者创建时,它的观察者列表是空集合。两个观察者相等的条件是执行equals时返回true。

public class Observable {
    private boolean changed = false;
    // 底层使用数组,并且线程安全
    private Vector obs;

    /**
     * 构造一个没有观察者的可观察者
     */
    public Observable() {
        obs = new Vector<>();
    }

    /**
     * 增加一个观察者,如果已存在,则什么都不做。
     * 如果o为null,抛出空指针异常。
     * @param o 将被添加的观察者
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    /**
     * 从观察者集合中删除一个观察者。
     * 如果o为null,无影响
     * @param o 将被删除的观察者
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }
    
    /**
     * 通知所有观察者,相当于notifyObservers(null)
     */
    public void notifyObservers() {
        notifyObservers(null);
    }

    /**
     * 通知所有观察者
     * @param o 可作为观察者update方法的参数
     */
    public void notifyObservers(Object arg) {
    	// 用于保存当前观察者集合的快照
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    /**
     * 移除所有观察者
     */
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    /**
     * 标记当前被观察者已经改变,调用hasChanged方法将返回true
     */
    protected synchronized void setChanged() {
        changed = true;
    }

    /**
     * 将当前被观察者标记为没有改变,调用hasChanged方法将返回false,执行notifyObservers时该方法会被自动调用
     */
    protected synchronized void clearChanged() {
        changed = false;
    }

    /**
     * 获取当前被观察者是否已经改变
     * @return true or false
     */
    public synchronized boolean hasChanged() {
        return changed;
    }

    /**
     * 返回观察者数量
     * @return 观察者数量
     */
    public synchronized int countObservers() {
        return obs.size();
    }
}

Observer(观察者/订阅者):

当想要收到可观察对象变化的通知时,类可以实现Observers接口。该接口只有一个update方法,每当观察到的对象被更改时,都会调用此方法。一个应用程序调用一个可观察到的对象的“notifyObservers”方法,让这个对象的所有观察者都知道这个变化。

public interface Observer {
    /**
     * This method is called whenever the observed object is changed. An
     * application calls an Observable object's
     * notifyObservers method to have all the object's
     * observers notified of the change.
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the notifyObservers
     *                 method.
     */
    void update(Observable o, Object arg);
}

使用

1、定义自己的被观察者对象:继承Observable类,然后按需要扩展字段和方法(引起对象变化的接口)。

例如:微信公众号提供了最新文章的字段,和发布文章的方法。

public class WeChatSubscription extends Observable {

	private String newest;
	
	/**
	 * 发布文章
	 * @param title 文章标题
	 */
	public void publish(String title) {
		System.out.println("微信公众号发布了文章:" + title);
		this.newest = title;
		// 设置改变
		setChanged();
		// 提醒观察者
		notifyObservers(title);
	}
	
	/**
	 * 提供数据的获取接口,以便观察者主动拉数据
	 * @return
	 */
	public String getNewest() {
		return newest;
	}
}

2、创建观察者对象:实现Observer接口,重写update方法。可按需扩展字段和方法。

例如:订阅者增加了被观察者的引用,以获得更加灵活的用法。

public class Subscriber implements Observer {
	
	Observable observable;
	
	/**
	 * 构造器,设置被观察者,并将自己设为观察者
	 * @param observable 被观察者
	 */
	public Subscriber(Observable observable) {
		this.observable = observable;
		observable.addObserver(this);
	}

	/**
	 * 当被观察者改变时,主动调用
	 */
	@Override
	public void update(Observable o, Object arg) {
		System.out.println("订阅者收到新文章:" + arg);
	}
	
	/**
	 * 用于主动从被观察者拉数据
	 * @return 最新文章标题
	 */
	public String get() {
		if (observable instanceof WeChatSubscription) {
			WeChatSubscription weChatSubscription = (WeChatSubscription) observable;
			return weChatSubscription.getNewest();
		}
		throw new UnsupportedOperationException("不支持的操作!");
	}

}

3、写测试。

public class Test {

	public static void main(String[] args) {
		// 创建可观察者:这里是微信公众号
		WeChatSubscription observable = new WeChatSubscription();
		
		// 创建观察者:这里是微信用户
		Subscriber observer = new Subscriber(observable);
		
		// 可观察者发生改变:发布新文章
		observable.publish("观察者模式");
		
		// 观察者自己拉取最新文章
		String title = observer.get();
		System.out.println("观察者自己拉取最新文章:" + title);

	}

}

4、结果。

微信公众号发布了文章:观察者模式
订阅者收到新文章:观察者模式
观察者自己拉取最新文章:观察者模式

总结

JDK提供了一套观察者模式,该工具的优点是:拥有比较完整的接口,继承或者实现之后无需过多处理就可以使用,而且可以实现观察者自己手动拉数据的功能。但是也存在确点,Observable是一个类,是“类”,所以,失去了灵活性。

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