观察者模式/Observer
意图/适用场景:
观察者模式的目的在地,当某一个对象(被观察者)的状态发生变化的时候,其它想要知晓这一改变的对象(观察者)能够得到通知。
能够实现这一点的设计方案有很多,但是为了使系统易于复用,应该选择低耦合度的设计方案。
UML:
参与者:
- 抽象观察者(Observer):所有观察者的公共接口。定义一个更新方法(即update()方法),当所观察的对象的状态发生改变时,这个方法是由被观察者所调用。
- 具体观察者(ConcreteObserver):实现Observer接口。具体化update()方法,当被观察者的状态发生变化时,在update()方法中作出相应的反应。
- 抽象被观察者(Observable):被观察者类的公共接口。定义一些管理观察者对象的方法,比如addObserver()、deleteObserver()等,所以它需要维护一个观察者对象的聚集。此外,在逻辑上,还需要实现“通知”的功能,即实现一个notify()方法,在其中调用聚集中观察者的update()方法。但是究竟在什么时候调用update(),这要看具体的实现而定,所以notify()方法并不是必须的。
- 具体被观察者(ConcreteObservable):实现Observable接口,具体化维护聚集的方法和通知的方法。在被观察状态发生改变时,调用观察者的update()方法。
要点:
观察者模式的核心概念就是被观察者对抽象观察者update()方法的调用。这种只面向“接口”的编程,把观察者和被观察者的耦合性降低,使它们可以互相不影响地变化和演绎。
要警惕通知过程中的循环调用。因为理论上说,一个类可以既是观察者又是被观察者,作用被观察者,它的观察者聚集里可以有作为观察者的它自己。这时就要保证在它的update()方法中不会再去调notify(),否则将造成死循环。
扩展:
抽象被观察者Observable并不是必须定义为接口,也可以把它定义为抽象类。这两种选择分别有如下的好处:
- 定义为接口:具体被观察者可以以自己的方式来维护观察者聚集,可以用Vector,也可以用Hashtable,或者别的。具体被观察者有很大的灵活性。
- 定义为抽象类:这时, 聚集由Observable抽象类来维护,notify()方法也在抽象类中实现。这样,所有的具体被观察者就不用再维护聚集了,这是一种比较方便的办法,但也缺少了一些灵活性。如果具体观察者各类较多,而且并不介意怎么维护聚集的话,这是一种好的选择。
应用实例:
Listener:
在使用AWT制作桌面应用的时候,都会使用某种Listener来监听窗口事件。“监听”和“观察”的原理是一样的,当某一事件发生的时候,向监听者发出通知。 应该说,监听是观察者模式的一个特例,它的特殊之处有两点:
- 观察的对象是“事件”。观察模式中,通知的源动力是一种广泛而谈的变化,事件可算作是一种特例。
- 在通知时,被监听者会把事件作为参数传给监听者。观察者模式中,并未指明update()方法是不是需要有参数,这依需要而定,可以有也可以没有。
观察者模式实现了一种“广播”的效果,应该场景比较广泛。
另外,在Java语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成了Java语言对观察者模式的支持。在需要使用这一模式时,可以考虑直接使用它们。
示例代码:
[java]
// Source code from file:ConcreteObservable.java
packagedesignPatterns.Observer;
import java.util.Enumeration;
import java.util.Vector;
publicclass ConcreteObservable implements Observable {
privateVector obs = new Vector();
publicvoid addObserver(Observer o) {
obs.add(o);
}
publicvoid deleteObserver(Observer o) {
obs.remove(o);
}
publicvoid notifyObservers() {
Enumerationenumeration = ((Vector)(obs.clone())).elements();
while(enumeration.hasMoreElements()){
Observeroo = (Observer)(enumeration.nextElement());
oo.update();
//((Observer)(enumeration.nextElement())).update();
}
}
publicvoid demo() {
intcount = 0;
while(count < 3) {
try{
Thread.sleep(1000);
notifyObservers();
}catch (Exception e) {
System.out.println(e);
}
}
}
}
// Source code from file:ConcreteObserverA.java
packagedesignPatterns.Observer;
publicclass ConcreteObserverA implements Observer {
publicvoid update() {
System.out.println("ConcreteObserverA got updated.");
}
}
// Source code from file:ConcreteObserverB.java
packagedesignPatterns.Observer;
publicclass ConcreteObserverB implements Observer {
publicvoid update() {
System.out.println("ConcreteObserverB got updated.");
}
}
// Source code from file:Observable.java
packagedesignPatterns.Observer;
publicinterface Observable {
publicvoid addObserver(Observer o);
publicvoid deleteObserver(Observer o);
publicvoid notifyObservers();
}
// Source code from file:Observer.java
packagedesignPatterns.Observer;
publicinterface Observer {
publicvoid update();
}
// Source code from file:User.java
packagedesignPatterns.Observer;
publicclass User {
publicstatic void main(String[] args) {
ConcreteObservable observable =new ConcreteObservable();
observable.addObserver(newConcreteObserverA());
observable.addObserver(newConcreteObserverB());
observable.demo();
}
}
[/java]