一、观察者模式的核心思想
观察者(Observer)模式又名发布一订阅(Publish/Subscribe)模式。GOF 给观察者模式如下定义:定义对象间的一种一对多的依赖关系,让多个观察者对象同时关注同一个对象,当该对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
如下图所示,观察者模式中包含5类对象。
- 目标接口 Subject:目标接口定义了增加观察者 attach()、删除观察者 detach()和通知观察者notifyObservers()3个接口,并定义自身的操作函数operation()。
- 抽象目标类 AbstractSubject:该类定义了观察者的集合对象 vect,用来存储被添加的观察者对象列表;并实现增加观察者 attach()、删除观察者 detach()和通知观察者 notifyObservers()3个函数来更新该列表,以及通知所有的观察者对象更新自己。
- 具体目标类MySubiect:继承自抽象目标类 AbstractSubiect,并编写具体的操作函数实现operation(),在该函数中可以调用 notifyObservers()来通知所有的观察者更新自己。
- 观察者接口Observer:为那些在目标类发生改变时需要获得通知的对象定义一个统一的接口 update(),当调用 notifyObservers()时将会调用 update()函数来更新自己。
- 具体观察者:可以定义多个观察者对象,如 Observer1、Observer2,用来编写统一的更新函数。客户端 Test 则可以通过为Subject类添加观察者 Observer 来实现监控。
下面来看具体的实现。
(1) 观察者接口 Observer.java定义了一个统一的更新接口update()。其源代码如下程序所示。
package behavior.observer;
/**
* @author Minggg
* 观察者接口
*/
public interface Observer {
public void update();
}
(2) 观察者实现类 Observer1.java 是一个观察者的具体实现,其更新函数用来往控制台输出一个字符串。其源代码如下程序所示。
package behavior.observer;
/**
* @author Minggg
* 具体观察者
*/
public class Observer1 implements Observer {
public void update(){
System.out.println("观察者1得到通知!");
}
}
(3) 观察者实现类 Observer2.java是另一个观察者的具体实现,其更新函数用来往控制台输出个字符串。其源代码如下程序所示。
package behavior.observer;
/**
* @author Minggg
* 具体观察者
*/
public class Observer2 implements Observer {
public void update(){
System.out.println("观察者2得到通知!");
}
}
(4) 被观察者接口Subject.java中定义了3个操作观察者的接口函数,并定义了一个具体的操作函数接口,表示自身的功能。其源代码如下程序所示。
package behavior.observer;
/**
* @author Minggg
* 被观察者接口
*/
public interface Subject {
// 增加观察者
public void attach(Observer observer);
// 删除观察者
public void detach(Observer observer);
// 通知所有观察者
public void notifyObservers();
// 自身的操作接口
public void operation();
}
(5) 被观察者抽象类 AbstractSubject.java中提供了一个 Vector列表 vect,用来保存所有的观察者对象,并编写实现函数来操作该列表对象。其源代码如下程序所示。
package behavior.observer;
import java.util.Enumeration;
import java.util.Vector;
/**
* @author Minggg
* 被观察者抽象类
*/
public abstract class AbstractSubject implements Subject {
private Vector<Observer> vect = new Vector<Observer>();
@Override
public void attach(Observer observer) {
vect.add(observer);
}
@Override
public void detach(Observer observer) {
vect.remove(observer);
}
@Override
public void notifyObservers() {
Enumeration<Observer> em = vect.elements();
while (em.hasMoreElements()) {
em.nextElement().update();
}
}
}
(6) 被观察者实现类 MySubject.java是一个具体的目标类,它仅仅实现自身的操作函数,并适时调用 notifyObservers()来通知所有的观察者。其源代码如下程序所示。
package behavior.observer;
/**
* @author Minggg
* 具体被观察者
*/
public class MySubject extends AbstractSubject {
public void operation() {
System.out.println("修改自己");
// 通知观察者
notifyObservers();
}
}
到此就完成了所有的开发。使用它们的方法也很简单,首先创建一个目标类MySubject 的对象subject,并可以为该对象添加多个观察者,当调用 operation()时会自动实现对观察者的通知。其源代码如下程序所示。
package behavior.observer;
public class Test {
public static void main(String[] args) {
Subject subject = new MySubject();
// 增加观察者
subjeet.attach(new Observer1());
subject.attach(new Observer2());
subject.operation();
}
}
运行该程序的输出如下:
修改自己
观察者1得到通知!
观察者2得到通知!
从程序的输出可以看出,通过给subject添加观察者对象,就实现了在自身操作时对观察者的通知。
二、何时使用观察者模式
Observer 模式的优点是实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制,类别清晰,并抽象了更新接口,使得可以有各种各样不同的观察者。因此,使用观察者模式可以实现业务与数据逻辑的通知机制。
观察者模式的效果有以下几个优点:
- 观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体现察者聚集,每一个具体现察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
- 观察者模式支持广播通信。被观察者会向所有的登记过的观察者发出通知。
观察者模式有下面的一些缺点:
- 如果一个被观察者对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很长时间。
- 如果在被观察者之间有循环依赖的话,被观察者会触发它们之间的循环调用,导致系统崩溃。
- 虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。
三、Java中的应用–Observable和 Observer 观密者实现
实际上,观察者模式在Java提供的开发包中已经提供了实现。在java.util 包中有如下两个类。
- Observer.java:一个观察者接口。
- Observable.java:一个被观察者对象。
Observer.java 方法与我们以上讲解的观察者接口类似,提供了一个更新方法。其源代码如下程序所示。
package java.util;
public interface Observer {
void update(Observable o, Object arg);
}
Observable.java 是一个观察者目标类,它也类似于上面提到的抽象观察者类,它提供了一个Vector 列表用来保存观察者对象,并提供了增加、删除、通知观察者的具体实现。源代码如下程序所示。
package java.util;
publie class Observable {
private boolean changed = false;
private Vector obs;
pubic Observable() {
obs = new Vector();
}
public synchronized void addObserver(Observer o) {
if (o == null) {
throw new NullPointerException();
}
if (!obs.contains(o)){
obs addElement(o);
}
}
pubfic synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
public void notifyObservers() {
notifyObservers(nul1);
}
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();
}
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();
}
}
因此,我们要使用观察者模式时,只需要使用以上两个类来编写自己的子类即可。