观察者模式/Observer

意图/适用场景:

观察者模式的目的在地,当某一个对象(被观察者)的状态发生变化的时候,其它想要知晓这一改变的对象(观察者)能够得到通知。

能够实现这一点的设计方案有很多,但是为了使系统易于复用,应该选择低耦合度的设计方案。

UML:

观察者模式/Observer_it

参与者:

  1. 抽象观察者(Observer):所有观察者的公共接口。定义一个更新方法(即update()方法),当所观察的对象的状态发生改变时,这个方法是由被观察者所调用。
  2. 具体观察者(ConcreteObserver):实现Observer接口。具体化update()方法,当被观察者的状态发生变化时,在update()方法中作出相应的反应。
  3. 抽象被观察者(Observable):被观察者类的公共接口。定义一些管理观察者对象的方法,比如addObserver()、deleteObserver()等,所以它需要维护一个观察者对象的聚集。此外,在逻辑上,还需要实现“通知”的功能,即实现一个notify()方法,在其中调用聚集中观察者的update()方法。但是究竟在什么时候调用update(),这要看具体的实现而定,所以notify()方法并不是必须的。
  4. 具体被观察者(ConcreteObservable):实现Observable接口,具体化维护聚集的方法和通知的方法。在被观察状态发生改变时,调用观察者的update()方法。

要点:

观察者模式的核心概念就是被观察者对抽象观察者update()方法的调用。这种只面向“接口”的编程,把观察者和被观察者的耦合性降低,使它们可以互相不影响地变化和演绎。

要警惕通知过程中的循环调用。因为理论上说,一个类可以既是观察者又是被观察者,作用被观察者,它的观察者聚集里可以有作为观察者的它自己。这时就要保证在它的update()方法中不会再去调notify(),否则将造成死循环。

扩展:

抽象被观察者Observable并不是必须定义为接口,也可以把它定义为抽象类。这两种选择分别有如下的好处:

  1. 定义为接口:具体被观察者可以以自己的方式来维护观察者聚集,可以用Vector,也可以用Hashtable,或者别的。具体被观察者有很大的灵活性。
  2. 定义为抽象类:这时, 聚集由Observable抽象类来维护,notify()方法也在抽象类中实现。这样,所有的具体被观察者就不用再维护聚集了,这是一种比较方便的办法,但也缺少了一些灵活性。如果具体观察者各类较多,而且并不介意怎么维护聚集的话,这是一种好的选择。

应用实例:

Listener:
在使用AWT制作桌面应用的时候,都会使用某种Listener来监听窗口事件。“监听”和“观察”的原理是一样的,当某一事件发生的时候,向监听者发出通知。 应该说,监听是观察者模式的一个特例,它的特殊之处有两点:

  1. 观察的对象是“事件”。观察模式中,通知的源动力是一种广泛而谈的变化,事件可算作是一种特例。
  2. 在通知时,被监听者会把事件作为参数传给监听者。观察者模式中,并未指明update()方法是不是需要有参数,这依需要而定,可以有也可以没有。

观察者模式实现了一种“广播”的效果,应该场景比较广泛。

另外,在Java语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成了Java语言对观察者模式的支持。在需要使用这一模式时,可以考虑直接使用它们。

示例代码:

   [java]
// Source code from file:  ConcreteObservable.java

package designPatterns.Observer;

import java.util.Enumeration;
import java.util.Vector;

public class ConcreteObservable implements Observable {

private Vector obs = new Vector();

public void addObserver(Observer o) {
obs.add(o);
}

public void deleteObserver(Observer o) {
obs.remove(o);
}

public void notifyObservers() {
Enumeration enumeration = ((Vector)(obs.clone())).elements();
while(enumeration.hasMoreElements()) {
Observer oo = (Observer)(enumeration.nextElement());
oo.update();
//                      ((Observer)(enumeration.nextElement())).update();
}
}

public void demo() {
int count = 0;
while (count < 3) {
try {
Thread.sleep(1000);
notifyObservers();
catch (Exception e) {
System.out.println(e);
}
}
}

}

// Source code from file:  ConcreteObserverA.java

package designPatterns.Observer;

public class ConcreteObserverA implements Observer {
public void update() {
System.out.println("ConcreteObserverA got updated.");
}
}

// Source code from file:  ConcreteObserverB.java

package designPatterns.Observer;

public class ConcreteObserverB implements Observer {
public void update() {
System.out.println("ConcreteObserverB got updated.");
}
}

// Source code from file:  Observable.java

package designPatterns.Observer;

public interface Observable {
public void addObserver(Observer o);
public void deleteObserver(Observer o);
public void notifyObservers();
}

// Source code from file:  Observer.java

package designPatterns.Observer;

public interface Observer {
public void update();
}

// Source code from file:  User.java

package designPatterns.Observer;

public class User {
public static void main(String[] args) {
ConcreteObservable observable = new ConcreteObservable();
observable.addObserver(new ConcreteObserverA());
observable.addObserver(new ConcreteObserverB());
observable.demo();
}
}
[/java]