1 意图
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
2 别名
依赖(Dependents), 发布-订阅(Publish-Subscribe)
3 动机
将一个系统分割成一系列相互协作的类有一个常见的副作用,需要维护相关对象间的一致性,我们不希望为此而使得各个类紧密耦合,因为这样降低了他们的可重用性。
Observer模式描述了如何建立这种关系,这一模式中的关键对象是目标(subject)和观察着(observer),一个目标可以有任意数目的依赖它的观察者。一旦目标的状态发生改变,所有的观察着都得到通知,作为对这个通知的响应,每个观察着都将查询目标以使其状态与目标的状态同步。这种交互也称为发布-订阅,目标是通知的发布者,它发出通知时病不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知
4 适用性
4.1 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中使他们可以各自独立的改变和复用
4.2 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变
4.3 当一个对象必须通知其他对象,而它不能假定其他对象是谁,换而言之,你不希望这些对象是紧密耦合的
5 结构
6 交互
7 效果
7.1 优点
7.1.1 目标和观察者之间的抽象耦合
7.1.2 支持广播通信
7.2 缺点
意外的更新:如果依赖准则的定义或维护不当,常常会引起错误的更新,这种错误是很难捕捉的。
8 实现
8.1 创建目标到其观察者之间的映射
8.1.1 显式的在目标中保存对他们的引用
8.1.2 时间换空间,用一个关联查找机制(例如hash表)来维护目标到观察者的映射
8.2 观察多个目标
扩展Update接口,以使观察者知道哪个目标送来的通知,目标对象可以简单的将自己作为Update操作的一个参数,让观察者知道该去检查哪一个目标
8.3 谁触发谁更新
8.3.1 由目标对象的状态设定操作在改变目标对象的状态后自动调用Notify,
8.3.1.1 优点:客户不需要记住要在目标对象上调用Notify
8.3.1.2 缺点:多个连续的操作会产生多次连续的更新,可能效率较低
8.3.2 让客户负责在适当的时候调用Notify
8.3.2.1 优点:客户可以在一系列状态改变完成后再一次性的触发更新,避免了不必要的中间更新
8.3.2.2 缺点:客户增加了触发更新的责任,客户可能忽而忘记调用Notify
8.4 对已经删除的目标的悬挂引用
8.4.1 当一个目标被删除时,让它通知它的观察者将对该目标的引用复位
8.5 在发出通知前确保目标的状态自身是一致的
在文档中记录哪一个subject操作触发通知总是应该的
8.6 避免特定于观察者的更新协议—推/拉模型
8.6.1 推模式:目标向观察者发送关于改变的详细信息,而不管它们需要与否。假定目标知道一些观察者需要的信息。可能使得观察者相对难以复用,因为目标对观察者的假定可能并不总是正确的。
8.6.2 拉模式:目标除了最小通知外什么也不送出,在此后由观察者显示的向目标询问细节。强调目标不知道她的观察者。可能效率较差,因为观察者对象需在没有目标对象帮助的情况下确定什么改变了
8.7 显示指定感兴趣的改变
8.7.1 扩展目标的注册接口,让各观察者注册为仅对特定事件感兴趣,以提高更新的效率。当一个事件发生时候,目标仅仅通知那些已注册为对该事件感兴趣的观察者。支持这种做法的一种途径,对使用目标对象的方面(aspects)的概念。可用下面代码将观察者对象注册为对目标对象的某特定事件感兴趣:
void Subject::Attach(Observer * , Aspect & interest)
通知时候,目标将这方面的改变作为Update操作的一个参数提供给他的观察者
void Observer::Update(Subject * ,Aspect & interest)
8.8 封装复杂的更新语义
更改管理器(ChangeManager):目的是尽量减少观察者反映其目标的状态变化所需的工作量,例如,如果一个操作涉及对几个相互依赖的目标进行改动,就必须保证仅仅在所有的目标都已经更改完毕后,才一次性的通知他们的观察者,而不是每个目标都通知观察者
ChangeManager的三个职责:
8.8.1 它将一个目标映射奥它的观察者并提供一个接口来维护这个映射,这就不需要由目标来维护对其观察者的引用,反之亦然
8.8.2 定义一个特定的更新策略
8.8.3 根据一个目标的请求,更新所有依赖于这个目标的观察者
9 相关模式
9.1 Mediator :通过封装复杂的更新语义,ChangeManager充当目标和观察者之间的中介者
9.2 Singleton :ChangeManager可使用Singleton模式来保证它是唯一的并且是可全局访问的
10 JDK中的Observer和Observable
一个 observable 对象可以有一个或多个观察者。观察者可以是实现了 Observer 接口的任意对象。一个 observable 实例改变后,调用 Observable 的 notifyObservers 方法的应用程序会通过调用观察者的 update 方法来通知观察者该实例发生了改变。
未指定发送通知的顺序。Observable 类中所提供的默认实现将按照其注册的重要性顺序来通知 Observers,但是子类可能改变此顺序,从而使用非固定顺序在单独的线程上发送通知,或者也可能保证其子类遵从其所选择的顺序。
注意,此通知机制与线程无关,并且与 Object 类的 wait 和 notify 机制完全独立。
Observable的方法摘要:
void | addObserver(Observer o) |
protected void | clearChanged() |
int | countObservers() |
void | deleteObserver(Observer o) |
void | deleteObservers() |
boolean | hasChanged() |
void | notifyObservers() |
void | notifyObservers(Object arg) |
protected void | setChanged() |
11 最简单的Observable实现:
package mvc.mobile.util;
/**
* <p>Title: </p>
*
* <p>Description:Observer interface for j2me</p>
*
* <p>Copyright: Copyright (c) 2009</p>
*
* <p>Company: </p>
*
* @author not attributable
* @version 1.0
*/
public interface Observer {
void update(Observable iTarget, Object iInterests);
}
package mvc.mobile.util;
import java.util.Vector;
/**
* <p>Title: </p>
*
* <p>Description: 最简单的Observable实现</p>
*
* <p>Copyright: Copyright (c) 2009</p>
*
* <p>Company: </p>
*
* @author not attributable
* @version 1.0
*/
public class Observable {
private boolean iChanged = false;
private Vector iObs ;
public Observable() {
//创建目标时候就新建一个观察者列表,注意这个列表需要同步
iObs = new Vector();
}
/**
* 添加观察者到观察者列表中
*/
public synchronized void addObserver(Observer aObserver){
if(aObserver == null )
return ;
if(!iObs.contains(aObserver))
iObs.addElement(aObserver);
}
/**
* 删除一个观察者
*/
public synchronized void deleteObserver(Observer aObserver){
iObs.removeElement(aObserver);
}
/**
* 通知操作,即被观察者发生变化,通知对应的观察者进行事先设定的操作,不传参数的通知方法
*/
public void notifyObserver(){
notifyObservers(null);
}
public void notifyObservers(Object aArg){
synchronized(this){
if(!iChanged)
return ;
clearChanged();
}
for(int i= iObs.size()-1; i>=0 ;i--)
((Observer)iObs.elementAt(i)).update(this,aArg);
}
protected synchronized void clearChanged(){
iChanged = false;
}
protected synchronized void setChanged(){
iChanged = true ;
}
public static void main(String[] args) {
Observable observable = new Observable();
}
}
测试用例:
package mvc.mobile.test.observerTest;
import mvc.mobile.util.Observer;
import mvc.mobile.util.Observable;
/**
* <p>Title: </p>
*
* <p>Description: </p>
*
* <p>Copyright: Copyright (c) 2009</p>
*
* <p>Company: </p>
*
* @author not attributable
* @version 1.0
*/
public class Friend implements Observer{
private String iName ;
public String getName() {
return iName;
}
public void setName(String aName){
iName = aName ;
}
public Friend(String aName) {
iName =aName ;
}
public void update(Observable iTarget, Object iInterests) {
if(iInterests instanceof Long)
System.out.println(iName+"已经知道你的手机号码改变为:"+iInterests);
}
}
package mvc.mobile.test.observerTest;
import mvc.mobile.util.Observable;
/**
* <p>Title: </p>
*
* <p>Description: </p>
*
* <p>Copyright: Copyright (c) 2009</p>
*
* <p>Company: </p>
*
* @author not attributable
* @version 1.0
*/
public class ModifyPhoneNum extends Observable{
public ModifyPhoneNum() {
super();
addFriend(new Friend("李培"));
addFriend(new Friend("晓红"));
}
public void addFriend(Friend aFriend){
super.addObserver(aFriend);
}
/**
* 手机号改变,通知好友
*/
public void modifyPhoneNum(final Long iPhoneNum){
/**
* 表明状态已经改变,不调用的话,不会通知观察者
*/
setChanged();
//通知其他好友自己的电话号码改变了
notifyObservers(iPhoneNum);
}
public static void main(String[] args) {
ModifyPhoneNum modifyphonenum = new ModifyPhoneNum();
modifyphonenum.modifyPhoneNum(new Long(123456789));
}
}