`
韩悠悠
  • 浏览: 842449 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

附:JAVA事件处理--观察者模式

 
阅读更多

简单地说,观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监察一个主题对象。这样一个主题对象在状态上的变化能够通知所有的依赖于此对象的那些观察者对象,使这些观察者对象能够自动更新。

  观察者模式的结构

  观察者(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、系统的类图。
分享到:
评论

相关推荐

    Java设计模式-图解-附代码

    - 观察者模式:定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。 - 备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存...

    Java设计模式-图解-附代码.doc

    本文件“Java设计模式-图解-附代码.doc”主要探讨了Java语言中的设计模式,分为创建型、结构型和行为型三大类。下面将对这些模式进行详细解释。 1. **创建型模式**: - **工厂方法**:提供一个接口用于创建对象,...

    观察者模式(附代码和解释)下载

    下面是一段简单的Java代码示例,展示了如何使用上述定义的接口和类来实现观察者模式。 ```java public class Client { public static void main(String[] args) { AbstractWatched watched = new ConcreteWatched...

    2020版23种Java设计模式-图解-附代码.pdf

    - **观察者模式**:定义对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。 - **责任链模式**:将请求的发送者和接收者解耦,通过链式结构传递请求,直到找到...

    java笔试面试大全-附答案

    9. **设计模式**:单例模式、工厂模式、观察者模式、装饰器模式等23种GOF设计模式,理解其应用场景和优缺点。 10. **Spring/Spring MVC**: - **依赖注入**:通过IOC(控制反转)容器管理对象及其依赖关系。 - **...

    java习题-多选题

    - 单例模式、工厂模式、观察者模式等常见的设计模式在Java编程中广泛应用,理解它们的原理和使用场景。 在"东软java笔试题+附答案"这个文件中,你可以找到这些问题的实例和解答,通过实际练习来巩固和提升你的Java...

    深入浅出设计模式之观察者模式总结(二)附例子

    观察者模式又称为发布-订阅模式或事件驱动模式,它由两部分组成:主题(Subject)和观察者(Observer)。主题是被观察的对象,可以是任何具有可变状态的对象;观察者是对主题状态变化感兴趣的对象,它们注册到主题上...

    Java设计模式 图解 附代码

    ##### 1.3.7 观察者模式 - **定义**:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 - **适用性**: - 当一个抽象模型有两个方面,其中一个方面...

    全网最齐全的Java面试题库-附答案-持续更新.zip

    9. **设计模式**:单例、工厂、观察者、装饰器、代理等常见设计模式的实现和应用。 10. **JVM**:JVM的工作原理,类加载机制,类加载器,类加载过程,JVM优化策略等。 11. **Spring框架**:IoC容器,AOP,事务管理...

    【电子版】校招面试题库(附答案与解析)java篇-破解密码.pdf

    - 设计模式:单例、工厂、观察者、装饰器等23种设计模式,是写出可维护、可扩展代码的关键。 通过深入学习和掌握上述知识点,不仅能够应对面试,还能为你的职业生涯打下坚实的基础。在准备面试的过程中,不仅要...

    Java酒店管理系统附设计文档

    6. **设计模式**:在开发过程中,可能会应用到如工厂模式(用于创建对象)、单例模式(确保类只有一个实例)、观察者模式(用于事件驱动)等设计模式,以提高代码的可读性和可维护性。 7. **文件操作**:系统可能...

    牛客面试真题附答案解析最新版-Java篇.rar

    8. **设计模式**:熟悉常见的23种设计模式,如单例、工厂、建造者、观察者、适配器等,并能根据场景灵活运用。 9. **Spring框架**:理解依赖注入原理,使用AOP进行切面编程,熟悉Spring Boot和Spring Cloud微服务...

    java与模式(附源码)

    观察者模式(Observer)让多个对象能够监听并响应某个对象的状态变化,这是事件驱动编程的基础。 "Java与模式"资料可能涵盖了23种GOF(GoF,Gang of Four)设计模式,包括上述提到的一些。每种模式都可能有详细的...

    23种模式详解-java附详细文档

    Java的`Observer`和`Observable`接口是实现观察者模式的基础。 21. **访问者模式**:在不改变类结构的前提下,增加新的操作。Java中较少直接使用,但在XML解析、DOM树操作等场景可见。 22. **状态模式**:允许对象...

    HeadFirst设计模式Java语言中文版附源码

    观察者模式是一种事件驱动的设计模式,一个对象的状态变化会通知所有依赖它的对象。这在GUI编程、事件处理、发布/订阅系统中十分常见。责任链模式则将请求传递给一系列处理者,直到找到合适的处理者为止,降低了对象...

    Java 最常见的 200+ 面试题:面试必备(附详解答案).zip

    - 观察者模式和装饰者模式:在实际开发中的应用。 7. **Java 8及以后的新特性**: - Lambda表达式:简洁的函数式编程风格。 - Stream API:处理集合数据的新方式。 - 默认方法:接口中增加非抽象方法。 - ...

    深入浅出设计模式附书源码Java版源码

    3. **行为型模式**:这类模式关注对象之间的交互和职责分配,例如策略模式(Strategy)、模板方法模式(Template Method)、观察者模式(Observer)、迭代器模式(Iterator)、访问者模式(Visitor)、责任链模式...

    全网最齐全的Java面试题库-附答案.zip

    例如,熟悉常见的设计模式(单例、工厂、观察者等)可以帮助你编写更优雅、可维护的代码。 总的来说,这份"全网最齐全的Java面试题库-附答案.zip"包含的资源将帮助你系统地复习和巩固Java相关的所有关键知识点,...

    【电子版】Java校招面试题库(附答案与解析)java篇.zip

    - 常见设计模式:单例、工厂、观察者、装饰器、适配器、策略等模式的理解和应用。 9. **JVM优化** - 垃圾回收算法:了解新生代、老年代的垃圾回收机制。 - 类加载机制:理解双亲委派模型和自定义类加载器。 - ...

Global site tag (gtag.js) - Google Analytics