观察者模式

观察者(Observer)模式

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

动机(motivation

在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。

使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

意图(Intent)

定义对象间的一种一对多的依赖关系,一便当一二对象的状态发生变化时,所有依赖于它的对象都得到通知并自动更新。

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使它们自动更新自己。

根据观察者对象引用的存储地点,观察者模式的类图有微妙的区别:

第一种:

       

 

这种实现在传统的模式著作和讨论中比较常见。

第二种:

                

 

Java语言提供对观察者模式的支持属于此种结构。

观察者模式的静态结构可以从类图中看清楚。

可以看出,在这个观察者模式的实现里有下面这些角色:

第一种情况:

抽象主题(Subject)角色:主题角色把所有对观察者对象的引用保存在一个聚集(比如Vector对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,主题角色又叫做抽象被观察者(Observable)角色,一般用一个抽象类或者一个接口实现。

抽象观察者(Observer)角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者角色一般用一个抽象类或者一个接口实现。

具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observable)。具体主题角色通常用一个具体子类实现。

具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保存一个指向具体主题对象的引用。具体观察角色通常用一个具体子类实现。

第二种情况:

如果仔细考察主题对象的功能时,可以发现它必须使用一个Java聚集来维护一个对象所有观察者对象的引用。而在前面所给出的实现里面,管理这个聚集的方法是由抽象主题角色声明并由具体主题角色实现的。这才导致了类图中从具体主题角色到抽象观察者角色的连线。

但是,一个自然的问题是,这些聚集管理方法难道在每一个具体主题角色中都不同吗?回答是否定的。第二种方案与第一种方案的主要区别就是代表存储观察者对象的聚集连线是从抽象主题到抽象观察者。显然,由于抽象主题角色必须实现一些方法,所以不能再使用Java接口,而应当采用一个抽象类。

Java语言提供的对观察者模式的支持

Java语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成Java语言对观察者模式的支持。

Observer接口

这个接口只定义了一个方法,即update()方法。当被观察者对象的状态发生变化时,被观察者对象的notifyObservers()方法就会调用这一方法。java.util提供的Observer接口的类图如下图所示:

                     

 

被观察者对象都是java.util.Observable类的子类。java.util.Observale提供的公开的方法支持观察者对象,这些方法中有两个对Observable的子类非常重要,一个是setChanged()被调用之后会设置一个内部标记变量,另一个是notifyObservers().第一个方法setChanged()被调用之后会设置一个内部标记变量,代表被观察者对象的状态发生了变化。第二个是notifyObservers(),这个方法被调用时,会调用所有等级过的观察者对象的update()方法,使这些观察者对象可以更新自己。

Java中的DEM事件机制

AWT中的DEM机制:

AWT1.0的事件处理模型时基于责任链的。这种模型不适用于复杂的系统,因此,在AWT1.1版本及以后的各个版本中,事件处理模型均为基于观察者模式的委派事件模型(Delegation Event ModelDEM)

DEM模型里,主题(Subject)角色负责发布(publish)事件,而观察者角色向特定的主题订阅(subscribe)它所感兴趣的事件。当一个具体主题产生一个事件时,它就会通知所有感兴趣的订阅者。

使用这种发布-订阅机制的基本设计目标是提供一种将发布与订阅者松散地耦合在一起的联系形式,以及一种动态地登记、取消向一个发布者的订阅请求的办法。显然,实现这一构思的技巧是设计抽象接口,并把抽象层和具体层分开。这在观察者模式里可以清楚看到。

软件系统中的事件处理允许两个或多个对象根据它们的状态的变化进行通信和协调,在常见的事件处理模型中,对象通常为划分为三种:事件对象,事件制造者对象和事件接受者对象,一般而言,某一对象是事件的对象的制造者,其余对象是事件的接收者;而事件对象本身则封装了有关事件的信息,当事件的制造者的内部的状态发生变化时,会根据需要创建一个代表其状态变化的事件对象,并将它传给所有登记过的事件接收者对象。

DEM的结构

事件源对象:一个类要成为事件源并不需要实现任何接口或者继承任何类,但是一个事件源需要保持一个事件监听器的列表。调用addXXXListener()方法增加一个监听器,调用removeXXXListener()方法删除一个监听器

 

事件对象:

DEM中,每一种事件都有一个事件对象与之对应,而所有的AWT中的事件对象都是从java.util.EventObject继承而来的,每一种具体的事件对象都有一些额外的功能,java.awt的各种事件类如下图:

     

由于旧事件模型的根节点叫做Event,因此在新的DEM事件模型中,事件对象的树结构的根节点叫做EventObject,以示区别。

AWT库中,事件类EventObject只有一个直接子类AWTEvent,而后者是所有的AWT事件类的起源。

Swing构件引进了更多的事件类,这些事件类不是从AWTEvent衍生出来的,而是直接从EventObject衍生出来的,同样,我们也可以设计自己的事件对象,直接继承自EventObject类。

事件对象封装了事件的原对象与事件监听器对象所需要的事件信息,有一些事件类,比如PaintEvent,是不会被传递给监听器的,因此对Java程序员来说并没有声明用处,Java应用软件程序设计师会接触到的是那些会被传递给监听器的事件类。

事件监听器对象

事件监听器对象是当事件发生时被调用的对象。一个对象要成为事件监听器对象,必须实现事件监听器接口,AWT库中所有的事件监听器都是java.util.EventListener接口的子接口,java.awt的各种事件监听器接口的类图如下所示:

     

应当指出的是,尽管Swing构件引入了很多的事件监听器接口,但是Swing仍然广泛使用AWT所提供的各种事件接口进行事件处理。

事件监听适配器:

这些适配器类为它们所实现的接口提供空的实现,这样一来,一个需要处理某个事件的应用类只需要继承相应的事件适配器类,并置换掉感兴趣的处理方法即可。

语义事件与底层事件

AWT区分了底层事件(Low-level Event)和语义事件(Semantic Event)。一个语义事件表达了用户所要做的事情,比如使用鼠标单击一个按键就是一个语义事件。相应地,ActionEvent就是一个语义事件,一个底层事情则是组成各种语义事件的单纯事件,比如鼠标单击事件有一个鼠标键按下事件和一个鼠标键释放释放事件组成,类似地调整滚动条是一个语义事件,而拖拽鼠标则是一个底层事件。

SAX2是怎么工作的

SAX2里面,XMLReader类扮演主体的角色,而org.xml.sax.ContentHandler接口扮演了观察者角色。AWTSAX2的最大区别是SAX2并不允许向每一个XMLReader对象登记多于一个的listener对象,这是与通常的观察者模式的最大区别。其次,SAX2并不使用java.util.EventObject对象或者它的子类,因为一个SAX2事件的信息均是XML文件的标示结构,可以直接传递个事件监听器对象中的合适的事件方法。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(java,swing,制造,存储,语言,Semantic)