简单地说,观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监察一个主题对象。这样一个主题对象在状态上的变化能够通知所有的依赖于此对象的那些观察者对象,使这些观察者对象能够自动更新。
观察者模式的结构 观察者(Observer)模式是对象的行为型模式,又叫做发表-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-收听者(Source/Listener)模式或从属者(Dependents)模式。
本模式的类图结构如下:
图1、观察者模式的静态结构可从类图中看清楚。 |
在观察者模式里有如下的角色:
.
抽象主题(Subject)角色:主题角色把所有的观察者对象的引用保存在一个列表里;每个主题都可以有任何数量的观察者。主题提供一个接口可以加上或撤销观察者对象;主题角色又叫做抽象被观察者(Observable)角色;
图2、抽象主题角色,有时又叫做抽象被观察者角色,可以用一个抽象类或者一个接口实现;在具体的情况下也不排除使用具体类实现。
|
. 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到通知时更新自己;
图3、抽象观察者角色,可以用一个抽象类或者一个接口实现;在具体的情况下也不排除使用具体类实现。
|
.
具体主题(ConcreteSubject)角色:保存对具体观察者对象有用的内部状态;在这种内部状态改变时给其观察者发出一个通知;具体主题角色又叫作具体被观察者角色;
图4、具体主题角色,通常用一个具体子类实现。 |
.具体观察者(ConcreteObserver)角色:保存一个指向具体主题对象的引用;和一个与主题的状态相符的状态。具体观察者角色实现抽象观察者角色所要求的更新自己的接口,以便使本身的状态与主题的状态自恰。
图5、具体观察者角色,通常用一个具体子类实现。 |
下面给出一个示意性实现的Java代码。首先在这个示意性的实现里,用一个Java接口实现抽象主题角色,这就是下面的Subject接口:
public interface Subject { public void attach(Observer
observer);
public void detach(Observer observer);
void
notifyObservers(); }
|
代码清单1、Subject接口的源代码。
这个抽象主题接口规定出三个子类必须实现的操作,即 attach() 用来增加一个观察者对象;detach()
用来删除一个观察者对象;和notifyObservers()
用来通知各个观察者刷新它们自己。抽象主题角色实际上要求子类保持一个以所有的观察者对象为元素的列表。
具体主题则是实现了抽象主题Subject接口的一个具体类,它给出了以上的三个操作的具体实现。从下面的源代码可以看出,这里给出的Java实现使用了一个Java向量来保存所有的观察者对象,而
attach() 和 detach() 操作则是对此向量的元素增减操作。
import java.util.Vector; import java.util.Enumeration;
public
class ConcreteSubject implements Subject { public void attach(Observer
observer) { observersVector.addElement(observer); }
public void
detach(Observer
observer) { observersVector.removeElement(observer); }
public
void notifyObservers() { Enumeration enumeration = observers(); while
(enumeration.hasMoreElements()) { ((Observer)enumeration.nextElement()).update(); } }
public
Enumeration observers() { return ((Vector)
observersVector.clone()).elements(); } private Vector observersVector =
new
java.util.Vector(); }
|
代码清单2、ConcreteSubject类的源代码。
抽象观察者角色的实现实际上是最为简单的一个,它是一个Java接口,只声明了一个方法,即update()。这个方法被子类实现后,一被调用便刷新自己。
public interface Observer { void
update(); } |
代码清单3、Observer接口的源代码。
具体观察者角色的实现其实只涉及update()方法的实现。这个方法怎么实现与应用密切相关,因此本类只给出一个框架。
public class ConcreteObserver implements Observer { public void
update() { // Write your code
here } } |
代码清单4、ConcreteObserver类的源代码。
虽然观察者模式的实现方法可以有设计师自己确定,但是因为从AWT1.1开始视窗系统的事件模型采用观察者模式,因此观察者模式在Java语言里的地位较为重要。正因为这个原因,Java语言给出了它自己对观察者模式的支持。因此,本文建议读者在自己的系统中应用观察者模式时,不妨利用Java语言所提供的支持。
Java语言提供的对观察者模式的支持 在Java语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成Java语言对观察者模式的支持。
Observer接口 这个接口只定义了一个方法,update()。当被观察者对象的状态发生变化时,这个方法就会被调用。这个方法的实现应当调用每一个被观察者对象的notifyObservers()方法,从而通知所有的观察对象。
图6、java.util提供的Observer接口的类图。
|
package java.util;
public interface Observer { /** *
当被观察的对象发生变化时,这个方法会被调用。 */ void update(Observable o, Object
arg); }
|
代码清单5、java.util.Observer接口的源代码。
Observable类 被观察者类都是java.util.Observable类的子类。java.util.Observable提供公开的方法支持观察者对象,这些方法中有两个对Observable的子类非常重要:一个是setChanged(),另一个是notifyObservers()。第一个方法setChanged()被调用之后会设置一个内部标记变量,代表被观察者对象的状态发生了变化。第二个是notifyObservers(),这个方法被调用时,会调用所有登记过的观察者对象的update()方法,使这些观察者对象可以更新自己。
java.util.Observable类还有其它的一些重要的方法。比如,观察者对象可以调用java.util.Observable类的addObserver()方法,将对象一个一个加入到一个列表上。当有变化时,这个列表可以告诉notifyObservers()方法那些观察者对象需要通知。由于这个列表是私有的,因此java.util.Observable的子对象并不知道观察者对象一直在观察着它们。
图7、Java语言提供的被观察者的类图。
|
被观察者类Observable的源代码:
package java.util; 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(); } }
|
代码清单6、java.util.Observer接口的源代码。
这个Observable类代表一个被观察者对象。一个被观察者对象可以有数个观察者对象,一个观察者可以是一个实现Observer接口的对象。在被观察者对象发生变化时,它会调用Observable的notifyObservers方法,此方法调用所有的具体观察者的update()方法,从而使所有的观察者都被通知更新自己。见下面的类图:
图8、使用Java语言提供的对观察者模式的支持。
|
发通知的次序在这里没有指明。Observerable类所提供的缺省实现会按照Observers对象被登记的次序通知它们,但是Observerable类的子类可以改掉这一次序。子类并可以在单独的线程里通知观察者对象;或者在一个公用的线程里按照次序执行。
当一个可观察者对象刚刚创立时,它的观察者集合是空的。两个观察者对象在它们的equals()方法返回true时,被认为是两个相等的对象。
怎样使用Java对观察者模式的支持 为了说明怎样使用Java所提供的对观察者模式的支持,本节给出一个非常简单的例子。在这个例子里,被观察对象叫做Watched,也就是被监视者;而观察者对象叫做Watcher。Watched对象继承自java.util.Obsevable类;而Watcher对象实现了java.util.Observer接口。另外有一个对象Tester,扮演客户端的角色。
这个简单的系统的结构如下图所示。
图9、一个使用Observer接口和Observable类的例子。
|
在客户端改变Watched对象的内部状态时,Watched就会通知Watcher采取必要的行动。
package com.javapatterns.observer.watching;
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."); } }
|
代码清单7、Tester类的源代码。
package com.javapatterns.observer.watching;
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(); } }
|
代码清单8、Watched类的源代码。
package com.javapatterns.observer.watching;
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() + "'"); } }
|
代码清单9、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.");
|
代码清单10、被观察者的内部状态发生了改变。
对应地,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.'
|
代码清单11、运行的结果。
菩萨的守瓶龟 想当年齐天大圣为解救师傅唐僧,前往南海普陀山请菩萨降伏妖怪红孩儿:“菩萨听说...恨了一声,将手中宝珠净瓶往海心里扑的一掼...只见那海当中,翻波跳浪,钻出个瓶来,原来是一个怪物驮着出来...要知此怪名和姓,兴风作浪恶乌龟。”
使用面向对象的语言描述,乌龟便是一个观察者对象,它观察的主题是菩萨。一旦菩萨将净瓶掼到海里,就象征着菩萨作为主题调用了notifyObservers()方法。在西游记中,观察者对象有两个,一个是乌龟,另一个是悟空。悟空的反应在这里暂时不考虑,而乌龟的反应便是将瓶子驮回海岸。
图10、菩萨和菩萨的守瓶乌龟。 |
菩萨作为被观察者对象,继承自Observable类;而守瓶乌龟作为观察者,继承自Observer接口;这个模拟系统的实现可以采用Java对观察者模式的支持达成。
Java中的DEM事件机制
AWT中的DEM机制
责任链模式一章中曾谈到,AWT1.0的事件处理的模型是基于责任链的。这种模型不适用于复杂的系统,因此在AWT1.1版本及以后的各个版本中,事件处理模型均为基于观察者模式的委派事件模型(Delegation
Event Model或DEM)。
在DEM模型里面,主题(Subject)角色负责发布(publish)事件,而观察者角色向特定的主题订阅(subscribe)它所感兴趣的事件。当一个具体主题产生一个事件时,它就会通知所有感兴趣的订阅者。
使用这种发布-订阅机制的基本设计目标,是提供一种将发布者与订阅者松散地耦合在一起的联系形式,以及一种能够动态地登记、取消向一个发布者的订阅请求的办法。显然,实现这一构思的技巧,是设计抽象接口,并把抽象层和具体层分开。这在观察者模式里可以清楚地看到。
使用DEM的用词,发布者叫做事件源(event source),而订阅者叫做事件聆听者(event
listener)。在Java里面,事件由类代表,事件的发布是通过同步地调用成员方法做到的。
Servlet技术中的的DEM机制
AWT中所使用的DEM事件模型实际上被应用到了所有的Java事件机制上。Servlet技术中的事件处理机制同样也是使用的DEM模型。
SAX2技术中的DEM机制
DEM事件模型也被应用到了SAX2的事件处理机制上。
观察者模式的效果
观察者模式的效果有以下的优点:
第一、观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。
由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。如果被观察者和观察者都被扔到一起,那么这个对象必然跨越抽象化和具体化层次。
第二、观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知,
观察者模式有下面的缺点:
第一、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
第二、如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。
第三、如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
第四、虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。
观察者模式与其它模式的关系
观察者模式使用了备忘录模式(Memento
Pattern)暂时将观察者对象存储在被观察者对象里面。
问答题
第一题、我和妹妹跟妈妈说:“妈妈,我和妹妹在院子里玩;饭做好了叫我们一声。”请问这是什么模式?能否给出类图说明?
问答题答案
第一题答案、这是观察者模式。我和妹妹让妈妈告诉我们饭做好了,这样我们就可以来吃饭了。换用较为技术化的语言来说,当系统的主题(饭)发生变化时,就告诉系统的其它部份(观察者们,也就是妈妈、我和妹妹),使其可以调整内部状态(有开始吃饭的准备),并采取相应的行动(吃饭)。
系统的类图说明如下。
图11、系统的类图。 |
相关推荐
**WCF观测者模式示例** Windows Communication Foundation (WCF) 是.NET框架中用于构建分布式应用程序的服务模型。它提供了一种强大的方式来实现服务之间的通信,而观测者模式(Observer Pattern)是设计模式的一种...
请根据观察者模式,用Java语言设计并实现气象站程序。其中,部分代码已经写好,包括: • 观察者接口 • 主题接口 • 显示板接口 • 测试程序 请在此框架下,继续完成CurrentConditionsDisplay、ForecastDisplay...
文中简要介绍了连续运行参考站系统(CORS)和河南省地质信息连续采集运行系统(HNGICS)的基本原理,通过对GPS网的"传统网观测模式"和"基于卫星连续运行基准站网的点观测模式"两种观测模式的对比分析,证明了"基于卫星连续...
观察者模式(Observer Pattern)是设计模式中的一种行为模式,它在Java中有着广泛的应用。这个模式的主要思想是定义一个一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。...
本实验报告旨在通过 PowerDesigner 和面向对象编程语言实现几种常见的结构型设计模式和行为型设计模式,包括外观模式、代理模式、职责链模式、命令模式和观测者模式。实验内容包括六个部分,分别是: 一、外观模式...
观察者模式在Java中扮演着极其重要的角色,尤其是在构建需要对数据变化做出即时响应的系统中。通过利用Java SDK提供的`Observable`类和`Observer`接口,开发人员可以很容易地在项目中实现这一模式。理解并正确使用...
### 定制 Java 观测器 #### 什么是 Java 观测器? Java 观测器是 ESRI 开发的一款基于 Java 的地理信息系统 (GIS) 应用程序,主要用于查看和交互式操作由 ArcIMS(一种用于发布地理信息的服务)提供的地图服务。它...
观察者模式(Observer Pattern)是一种行为设计模式,它允许你定义一个订阅机制,可以在对象状态改变时通知多个“观察”该对象的其他对象。在Java中,这种模式被广泛应用于事件处理,例如JavaBeans事件。 JavaBeans...
- `META-INF`:这是Java类库的标准目录,通常包含版本信息、服务提供者等元数据。 - `com`:这是一个标准的Java包名,可能包含了jieba分词器的Java源代码和相关类。 3. **使用jieba分词器**: 在Java项目中,...
在开发观测数据监控软件时,Java语言以其强大的性能、跨平台能力和丰富的库支持成为首选工具。本项目将深入探讨如何利用Java技术栈构建一个高效、可靠的监控系统,以收集、处理和展示各种观测数据。 首先,Java的...
Java编程语言在数据分析和统计处理领域中也有广泛的应用,特别是在与SPSS(Statistical Package for the Social Sciences)软件的交互方面。标题提到的"JAVA生成SAV格式的Util"是关于利用Java编写工具类或者库来创建...
在这个实战教程中,我们将学习如何使用Java编程语言创建一个天文观测应用程序。首先,我们需要了解Java的基础知识,包括类、对象和集合,这是构建任何Java应用的基础。Java开发环境的准备至关重要,确保安装了...
该系统采用B/S模式(浏览器/服务器模式)进行开发,利用Java语言进行编码实现。系统包含多个核心模块,其中包括天气预报模块、自然灾害预警模块和全局观测模块,用于为城市提供气象信息服务,并在预防城市自然灾害中...
使用Java内建的观察者模式 java.util.Observable的黑暗面 设计箱内的工具 习题解答 …… 3 装饰者模式:装饰对象 4 工厂模式:烘烤OO的精华 5 单件模式:独一无二的对象 6 命令模式:封装调用 7 ...
一个HTA的工具,使用美国变星观测者协会的基本计划建立一个观测计划.hta
《深入理解Headfirst策略模式——基于Java代码实例》 策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在Headfirst的讲解中,这个模式被巧妙地通过鸭子的例子来阐述,使得复杂的概念变得生动易懂。在...
这个Java实现的DEMO将帮助我们理解如何在编程环境中构建和应用ARIMA模型进行时间序列预测。以下是关于ARIMA模型和Java实现的相关知识点: 1. **ARIMA模型介绍**: - **自回归项(AR)**:ARIMA模型基于历史观测值...
在本文档中,主要探讨了利用Java语言开发观测数据监控软件的研究,涉及到的关键技术、开发方法、应用前景等多个方面。 首先,随着气象业务水平的提高和观测业务改革的加快,自动气象站在全国各地的台站投入使用,...
1. **最小二乘法**:最小二乘法是一种优化技术,其目标是通过调整模型参数,使得模型预测值与实际观测值之间的残差平方和最小。在曲线拟合中,我们寻找一条直线或曲线,使得所有数据点到这条线的垂直距离的平方和...
基于JAVA的气象站数据监控平台的设计与实现.pdf