Java观察者模式(Observer)

    Observer模式的意图是“定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新”。从这段话里我们可以得到两个信息,如下:

     1)观察者(Observer):具体执行操作的对象,有多个;

      2)被观察者(Observable):被观察的对象,如果该对象发生某些变化则通知观察者执行对应的操。

      观察者(Observer)模式是对象的行为型模式,又叫做发表-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-收听者(Source/Listener)模式或从属者(Dependents)模式。

      JDK里提供的Observer设计模式的实现由java.util.Observable类和 java.util.Observer接口组成。从名字上可以清楚的看出两者在Observer 设计模式中分别扮演的角色:Observer是观察者角色,Observable是被观察目标(subject)角色。以下分析JDK中的观察者模式:

      ① Oberver接口:这个接口只有一个尚未实现的抽象方法update。实现该接口的对象成为观察者,该对象要实现update方法。当被观察者对象的状态发生变化时,通过调用执行每一个被观察者的notifyObservers()方法,从而使得观察者对象中的update方法被执行,进而更新观察者的状态

      ② 被观察者类都是java.util.Observable类的子类。java.util.Observable提供公开的方法支持观察者对象,这些方法中有两个对Observable的子类非常重要:一个是setChanged(),另一个是notifyObservers()。第一个方法setChanged()被调用之后会设置一个内部标记变量,代表被观察者对象的状态发生了变化。第二个是notifyObservers(),这个方法被调用时,会调用所有登记过的观察者对象的update()方法,使这些观察者对象可以更新自己的状态。     java.util.Observable类还有其它的一些重要的方法。比如,观察者对象可以调用java.util.Observable类的addObserver()方法,将对象一个一个加入到一个列表上。当有变化时,这个列表可以告诉notifyObservers()方法那些观察者对象需要通知。由于这个列表是私有的,因此java.util.Observable的子对象并不知道观察者对象一直在观察着它们。

    Observable类中的方法:

    1)void addObserver(Observer o)  如果观察者与集合中已有的观察者不同,则向对象的观察者集中添加此观察者。未指定向多个观察者发送通知的顺序。

   2)protected void clearChanged() 指示对象不再改变,或者它已对其所有的观察者通知了最近的改变,所以 hasChanged 方法将返回 false。notifyObservers 方法自动调用此方法。

   3)int countObservers() 返回 Observable 对象的观察者数目。

   4)void deleteObserver(Observer o) 从对象的观察者集合中删除某个观察者。向此方法传递 null无效。

   5)void deleteObservers()清除观察者列表,使此对象不再有任何观察者。

   6)boolean hasChanged()测试对象是否改变。当且仅当在此对象上最近调用了setChanged方法时才返回true;否则返回false。

   7)void notifyObservers() 如果hasChanged方法指示对象已改变,则通知其所有观察者,并调用clearChanged方法来指示此对象不再改变。每个观察者都有其update方法,其调用参数有两个:observable对象和null。换句话说,此方法等效于:notifyObservers(null)。

   8)void notifyObservers(Object arg)如果hasChanged方法指示对象已改变,则通知其所有观察者,并调用clearChanged方法来指示此对象不再改变。每个观察者都有其 update 方法,其调用参数有两个:observable对象和arg参数。arg可以是任意对象

   9)protected void setChanged() 标记此 Observable 对象为已改变的对象;现在 hasChanged 方法将返回 true。

      Observable源码:

import java.util.Vector;

public class Observable {
	private boolean changed = false;
	private Vector obs;

	/** 用0个观察者构造一个被观察者。 **/
	public Observable() {
		obs = new Vector();
	}
	/**
	 * 将一个观察者加到观察者列表上面。
	 */
	public synchronized void addObserver(Observer o) {
		if (!obs.contains(o)) {
			obs.addElement(o);
		}
	}

	/**
	 * 将一个观察者对象从观察者列表上删除。
	 */
	public synchronized void deleteObserver(Observer o) {
		obs.removeElement(o);
	}

	/**
	 * 相当于 notifyObservers(null)
	 */
	public void notifyObservers() {
		notifyObservers(null);
	}

	/**
	 * 如果本对象有变化(那时hasChanged 方法会返回true) 调用本方法通知所有登记在案的观察者,即调用它们的update()方法,
	 * 传入this和arg作为参量。
	 */
	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();
	}

	/**
	 * 将“已变化”设为true
	 */
	protected synchronized void setChanged() {
		changed = true;
	}

	/**
	 * 将“已变化”重置为false
	 */
	protected synchronized void clearChanged() {
		changed = false;
	}

	/**
	 * 探测本对象是否已变化
	 */
	public synchronized boolean hasChanged() {
		return changed;
	}

	/**
	 * 返还被观察对象(即此对象)的观察者总数。
	 */
	public synchronized int countObservers() {
		return obs.size();
	}
}

     Oberver接口

     这是个接口类,这个接口只有一个为实现的抽象方法update。实现该接口的对象成为观察者,该对象要实现update方法。注册了该对象(观察者)的对象(观察者)实例条用notifiyObservers方法后,观察者会自动执行update方法。

     void update(Observable o, Object arg) 只要改变了 observable 对象就调用此方法。o - observable 对象。arg - notifyObservers 方法的参数。

      Observable类代表一个被观察者对象。一个被观察者对象可以有数个观察者对象,一个观察者可以是一个实现Observer接口的对象。在被观察者对象发生变化时,它会调用Observable的notifyObservers方法,此方法调用所有的具体观察者的update()方法,从而使所有的观察者都被通知更新自己。见下面的类图:
 

      Observable 类中所提供的默认实现将按照其注册的重要性顺序来通知 Observers,但是子类可能改变此顺序,从而使用非固定顺序在单独的线程上发送通知,或者也可能保证其子类遵从其所选择的顺序。Observerable类所提供的缺省实现会按照Observers对象被登记的次序通知它们,但是Observerable类的子类可以改掉这一次序。子类并可以在单独的线程里通知观察者对象;或者在一个公用的线程里按照次序执行。 当一个可观察者对象刚刚创立时,它的观察者集合是空的。两个观察者对象在它们的equals()方法返回true时,被认为是两个相等的对象。 

================================================================================================================================

     为了说明怎样使用Java所提供的对观察者模式的支持,本节给出一个非常简单的例子。在这个例子里,被观察对象叫做Watched,也就是被监视者;而观察者对象叫做Watcher。Watched对象继承自java.util.Obsevable类;而Watcher对象实现了java.util.Observer接口。另外有一个对象Tester,扮演客户端的角色。 这个简单的系统的结构如下图所示。

在客户端改变Watched对象的内部状态时,Watched就会通知Watcher采取必要的行动。 程序的源码清单如下所示:

Observable类(被观察者),本例中为Watched:

import java.util.Observable;

public class Watched extends Observable {
	private String data = "";

	public String retrieveData() {
		return data;
	}

	public void changeData(String data) {
		if (!this.data.equals(data)) {
			this.data = data;
			setChanged();
		}
		notifyObservers();
	}
}

Observer类(观察者类),本例中为Watcher:

import java.util.Observable;
import java.util.Observer;

public class Watcher implements Observer {
	public Watcher(Watched w) {
		//将观察者加入被观察者对象中的观察者列表中
		w.addObserver(this);
	}

	public void update(Observable ob, Object arg) {
		System.out.println("Data has been changed to: '"
				+ ((Watched) ob).retrieveData() + "'");
	}
}

客户端代码:

import java.util.Observer;

public class Tester {
	static private Watched watched;
	static private Observer watcher;

	public static void main(String[] args) {
		watched = new Watched();
		watcher = new Watcher(watched);
		watched.changeData("In C, we create bugs.");
		watched.changeData("In Java, we inherit bugs.");
		watched.changeData("In Java, we inherit bugs.");
		watched.changeData("In Visual Basic, we visualize bugs.");
	}
}

执行结果:可以看出,虽然客户端将Watched对象的内部状态赋值了四次,但是值的改变只有三次:Watcher对象汇报了三次改变,下面就是运行时间程序打印出的信息:

Data has been changed to: 'In C, we create bugs.'
Data has been changed to: 'In Java, we inherit bugs.'
Data has been changed to: 'In Visual Basic, we visualize bugs.'

AWT中的DEM机制
  在DEM模型里面,主题(Subject)角色负责发布(publish)事件,而观察者角色向特定的主题订阅(subscribe)它所感兴趣的事件。当一个具体主题产生一个事件时,它就会通知所有感兴趣的订阅者。 
  使用这种发布-订阅机制的基本设计目标,是提供一种将发布者与订阅者松散地耦合在一起的联系形式,以及一种能够动态地登记、取消向一个发布者的订阅请求的办法。显然,实现这一构思的技巧,是设计抽象接口,并把抽象层和具体层分开。这在观察者模式里可以清楚地看到。 
  使用DEM的用词,发布者叫做事件源(event source),即为发起时间动作的一方Observable,而订阅者叫做事件聆听者(event listener),时间监听器,即为Observer。在Java里面,事件由类代表,事件的发布是通过同步地调用成员方法做到的。 
Servlet技术中的的DEM机制:AWT中所使用的DEM事件模型实际上被应用到了所有的Java事件机制上。Servlet技术中的事件处理机制同样也是使用的DEM模型。 
SAX2技术中的DEM机制:DEM事件模型也被应用到了SAX2的事件处理机制上。 
观察者模式的效果有以下的优点 
  第一、观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。 客户端不再依赖于观察器,因为通过使用主体和 Observer 接口对客户端进行了隔离。许多框架具有此优点,在这些框架中的应用程序组件可以注册为当(低级)框架事件发生时得到通知。结果,框架将调用应用程序组件,但不会依赖于它。
  由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。如果被观察者和观察者都被扔到一起,那么这个对象必然跨越抽象化和具体化层次。 
  第二、观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知, 

       第三、观察器数目可变。观察器可以在运行时附加和分离,因为主体对于观察器数目没有任何假定。此功能在这样的情况下是很有用的:观察器数在设计时是未知的。例如,如果用户在应用程序中打开的每个窗口都需要一个观察器。

观察者模式有下面的缺点 

     第一、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 【在许多实现中,观察器的 update() 方法可能与主体在同一线程中执行。如果观察器列表很长,则执行 Notify() 方法可能需要很长时间。抽取对象依赖性并不意味着添加观察器对应用程序没有任何影响。】
  第二、如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。【在 Observer 中使用的回调机制(当对象注册为以后调用时)会产生一个常见的错误,从而导致内存泄漏,甚至是在托管的 C# 代码中。假定观察器超出作用范围,但忘记取消对主体的订阅,那么主体仍然保留对观察器的引用。此引用防止垃圾收集在主体对象也被破坏之前重新分配与观察器关联的内存。如果观察器的生存期比主体的生存期短得多(通常是这种情况),则会导致严重的内存泄漏。】
  第三、如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。 
  第四、虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。 

      第五、隐藏的依赖项。观察器的使用将显式依赖性(通过方法调用)转变为隐式依赖性(通过观察器)。如果在整个应用程序中广泛地使用观察器,则开发人员几乎不可能通过查看源代码来了解所发生的事情。这样,就使得了解代码更改的含意非常困难。此问题随传播级别急剧增大(例如,充当 Subject 的观察器)。因此,应该仅在少数定义良好的交互(如 Model-View-Controller 模式中模型和视图之间的交互)中使用观察器。最好不要在域对象之间使用观察器。

     第六、测试 / 调试困难。尽管松耦合是一项重大的体系结构功能,但是它可以使开发更困难。将两个对象去耦的情况越多,在查看源代码或类的关系图时了解它们之间的依赖性就越难因此,仅当可以安全地忽略两个对象之间的关联时才应该将它们松耦合(例如,如果观察器没有副作用)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值