Observer模式也叫观察者模式,是由GoF提出的23种软件设计模式的一种。Observer模式是行为模式之一,它的作用是当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对象状态。
本文介绍设计模式中的(Observer)模式的概念,用法,以及实际应用中怎么样使用Observer模式进行开发。
Observer模式的概念
Observer模式是行为模式之一,它的作用是当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对象状态。
Observer模式提供给关联对象一种同步通信的手段,使某个对象与依赖它的其他对象之间保持状态同步。
Observer模式的典型应用
- 侦听事件驱动程序设计中的外部事件
- 侦听/监视某个对象的状态变化
- 发布者/订阅者(publisher/subscriber)模型中,当一个外部事件(新的产品,消息的出现等等)被触发时,通知邮件列表中的订阅者
Observer模式的优点
- 对象之间可以进行同步通信
- 可以同时通知一到多个关联对象
- 对象之间的关系以松耦合的形式组合,互不依赖
Observer模式的结构

Observer模式的角色:
Subject(被观察者)
被观察的对象。当需要被观察的状态发生变化时,需要通知队列中所有观察者对象。Subject需要维持(添加,删除,通知)一个观察者对象的队列列表。
ConcreteSubject
被观察者的具体实现。包含一些基本的属性状态及其他操作。
Observer(观察者)
接口或抽象类。当Subject的状态发生变化时,Observer对象将通过一个callback函数得到通知。
ConcreteObserver
观察者的具体实现。得到通知后将完成一些具体的业务逻辑处理。
JDK对Observer模式的支持
JDK提供了对observer设计模式的支持:
- 通过java.util.Observable类扮演Subject角色,一个类只需通过继承java.util.Observable类便可担当ConcreteSubject角色;
- 通过java.util.Observer接口扮演Observer角色,一个类只需实现java.util.Observer接口便可担当ConcreteObserver角色。
- java.util.Observable的实现类通过调用setChanged()方法以及notifyObservers(Object)方法便可简单地通知Observer。
当然,我们也可以不使用JDK提供的类与接口而自己实现Observer模式。
Observer模式的应用范例
下面,我们来实现一个Observer模式用来模拟新闻订阅操作:
当一个新闻被发表时,系统一方面需要通知管理者审阅;另一方面需要通知订阅者。
文件一览:
Client
测试类。
NewsPublisher
相当于ConcreteSubject角色。该类继承相当于Subject角色的Observable类。
SubscriberObserver
相当于ConcreteObserver角色。该类实现了相当于Observer角色的IObserver接口。当 NewsPublisher对象发生变化时得到通知,并向订阅者发送订阅邮件。
ManagerObserver
相当于ConcreteObserver角色。该类实现了相当于Observer角色的IObserver接口。当 NewsPublisher对象发生变化时得到通知,并向管理者发送消息邮件。
News
封装了新闻数据的类,与Observer模式无直接关系。
Observable
Subject角色
Observer
Observer角色
下面看具体代码实现:
Client
package zieckey.designpatterns.study.observer.nojdk;
public class Client {
/** * Test Observer Pattern * -- * */ public static void main( String[] args ) { NewsPublisher publisher = new NewsPublisher(); //添加观察者对象
publisher.register( new SubscriberObserver() ); publisher.register( new ManagerObserver() );
//发布新闻,触发通知事件
publisher.publishNews( "Hello news", "news body" ); } }
|
NewsPublisher
package zieckey.designpatterns.study.observer.nojdk;
/** * Subject ROLE * NewsPublisher: news publisher * */ public class NewsPublisher extends Observable { public void publishNews( String newsTitle, String newsBody ) { News news = new News( newsTitle, newsBody );
System.out.println( "News published:" + newsTitle ); this.notifyAllObervers( news ); //通知各Observer,并发送一个名为news对象的消息
//other process ... such as save news to database
} }
|
SubscriberObserver
package zieckey.designpatterns.study.observer.nojdk;
/** * Observer ROLE * */ public class SubscriberObserver implements IObserver {
public void update( Observable observee, Object param ) { if ( param instanceof News ) { mail2Subscriber( (News)param ); } }
private void mail2Subscriber( News news ) { System.out.println( "Mail to subscriber. A news published with title : " + news.getTitle() ); } }
|
ManagerObserver
package zieckey.designpatterns.study.observer.nojdk;
/** * Observer ROLE * */ public class ManagerObserver implements IObserver {
public void update( Observable observee, Object param ) { if ( param instanceof News ) { mail2Manager( (News)param ); } }
private void mail2Manager( News news ) { System.out.println( "Mail to Manager. A news published with title : " + news.getTitle() ); } }
|
News
package zieckey.designpatterns.study.observer.nojdk;
//data entity
public class News { private String title; private String body;
public News( String title, String body ) { this.title = title; this.body = body; }
public String getBody() { return body; }
public void setBody( String body ) { this.body = body; }
public String getTitle() { return title; }
public void setTitle( String title ) { this.title = title; } }
|
Observable
package zieckey.designpatterns.study.observer.nojdk;
import java.util.ArrayList;
/** * Subject(被观察者) * 被观察的对象。 * 当需要被观察的状态发生变化时,需要通知队列中所有观察者对象。 * Subject需要维持(添加,删除,通知)一个观察者对象的队列列表。 * * @author zieckey * */ public class Observable { private ArrayList < IObserver > arrayList = null;
Observable( ) { arrayList = new ArrayList < IObserver >(); }
public void register( IObserver observer ) { arrayList.add( observer ); }
public void unregister( IObserver observer ) { arrayList.remove( observer ); }
public void notifyAllObervers( Object arg ) { for ( int i = 0; i < arrayList.size(); i++ ) { arrayList.get( i ).update( this, arg ); } }
}
|
IObserver
package zieckey.designpatterns.study.observer.nojdk;
/** * * Observer(观察者) * 接口或抽象类。 * 当Subject的状态发生变化时,Observer对象将通过一个callback函数得到通知。 * * @author zieckey * */ public interface IObserver { public void update( Observable observee, Object param ); }
|
运行程序输出:
News published:Hello news Mail to subscriber. A news published with title:Hello news Mail to Manager. A news published with title : Hello news
|
应用场景和优缺点
上面已经对观察者模式做了比较详细的介绍,还是那句话,人无完人,模式也不是万能的,我们要用好设计模式来解决我们的实际问题,就必须熟知模式的应用场景和优缺点:
观察者模式的应用场景:
1、 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
2、 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
观察者模式的优点:
1、 Subject和Observer之间是松偶合的,分别可以各自独立改变。
2、 Subject在发送广播通知的时候,无须指定具体的Observer,Observer可以自己决定是否要订阅Subject的通知。
3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
观察者模式的缺陷:
1、 松偶合导致代码关系不明显,有时可能难以理解。(废话)
2、 如果一个Subject被大量Observer订阅的话,在广播通知的时候可能会有效率问题。(毕竟只是简单的遍历)
相关原则
相信大家现在对观察者模式都应该很清楚了吧!OK!那么,就像我们在前面的文章里反复强调的一样,设计原则远比模式重要,学习设计模式的同时一定要注意体会设计原则的应用。这里我们再来看看观察者模式里都符合那些设计原则。
1、 Identify the aspects of your application that vary and separate them from what stays the same. (找到系统中变化的部分,将变化的部分同其它稳定的部分隔开。)
在观察者模式的应用场景里变化的部分是Subject的状态和Observer的数量。使用Observer模式可以很好地将这两部分隔离开,我们可以任意改变Observer的数量而不需要去修改Subject,而Subject的状态也可以任意改变,同样不会对其Observer有任何影响。
2、 Program to an interface,not an implementation.(面向接口编程,而不要面向实现编程。)
Subject和Observer都使用接口来实现。Subject只需要跟踪那些实现了IObserver接口的对象,所以其只依赖于IObserver;而所有Observer都通过ISubject接口来注册、撤销、接收通知,所以它们也只依赖于 ISubject;所以这是面向接口编程的,这样的实现方式使得Subject和Observer之间完全没有任何耦合。
3、 Favor composition over inheritance.(优先使用对象组合,而非类继承)
观察者模式使用对象组合将Subject和若干observer联系起来。它们之间的关系不是通过类的继承而是在运行时的动态组合。