您的位置:首页 > 编程语言 > Java开发

《Java设计模式》之观察者模式(1)

2015-08-17 09:09 731 查看

1模式概述

虽然设计模式并不是万能丹, 但确实是一个非常强大的工具,开发人员或架构师可使用它积极地参与任何项目。设计模式可确保通过熟知和公认的解决方案解决常见问题。模式存在的事实基础在 于:大多数问题,可能已经有其他个人或开发小组解决过了。因此,模式提供了一种在开发人员和组织之间共享可使用解决方案的形式。无论这些模式的出处是什 么,这些模式都利用了大家所积累的知识和经验。这可确保更快地开发正确的代码,并降低在设计或实现中出现错误的可能性。此外,设计模式在工程小组成员之间 提供了通用的术语。参加过大型开发项目的人员都知道,使用一组共同的设计术语和准则对成功完成项目来说是至关重要的。最重要的是,如果能正确地使用,设计 模式可以节省您大量的时间。
要想在设计中正确地运用一个设计模式,需要考虑以下条件:
1. 弄清你的问题的本质;
2. 了解这个模式;
3. 理解这个模式如何解决你的问题。

2 Observer模式原理简介

2.1 基本原理描述

Observer模式是一种常用的设计模式, 尤其是在界面设计中被广泛应用。
Observer模式的好处是:它解除了观察者和目标之间的耦合关系。目标不需要知道它的观察者的任何信息。相反,目标只是允许观察者订阅事件。当目标产生一个事件时,它简单地将事件传给每一个观察者。
简单地说,观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监察一个主题对象。这样一个主题对象在状态上的变化能够通知所有的依赖于此对象的那些观察者对象,使这些观察者对象能够自动更新。
在设计一组依赖的对象与它们所依赖的对象之间一致(同步)的交互模型时, 观察者模式(Observer Pattern)很有用。它可以使依赖对象的状态与它们所依赖的对象的状态保持同步。这组依赖的对象指的是观察者(Observer),它们所依赖的对象 称为主题(Subject)。为了实现观察者(Observer)与主题(Subject)的状态保持同步,应使用观察者模式。
观察者(Observer)模式是对象的行为型模式,又叫做发表-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-收听者(Source/Listener)模式或从属者(Dependents)模式。推荐采用发布者--订阅者(publisher--subscriber)模型,以使这组观察者和主题对象之间有清晰的界限, 对应关系为:
publisher--subscriber -->> Subject--Observer。
典型的观察者(Observer)是一个依赖于或者关注于某个或某些主题对象的状态的对象。
主题需维护一个动态的观察者链表,关注于该主题状态的其他观察者对象需注册自己为该主题的观察者。主题状态发生的变化,都需要通知所有的已注册的观察者。当观察者从主题接到通知以后,每一个观察者应该查询主题,使自己的状态与主题的同步。因此一个主题扮演着发布者的角色,发布信息到所有的已订阅该主题状态的观察者。
一个主题可以有一个或者多个观察者,主题和它的观察者之间包含了一对多的关系。当主题的实例的状态发生变化时,所有的依赖于它的观察者都会得到通知并更新自己。
一个观察者可以注册或者订阅多个主题。当观察者不希望再得到通知时,它可以向主题进行注销

3 Observer角色分析

在观察者模式里有如下的角色:

抽象主题(Subject)角色
主题角色把所有的观察者对象的引用保存在一个列表里;每个主题都可以有任何数量的观察者。主题提供一个接口可以加上或撤销观察者对象;主题角色又叫做抽象被观察者(Observable)角色;
抽象主题角色,有时又叫做抽象被观察者角色,可以用一个抽象类或者一个接口实现;在具体的情况下也不排除使用具体类实现。
抽象观察者(Observer)角色
为所有的具体观察者定义一个接口,在得到通知时更新自己; 抽象观察者角色,可以用一个抽象类或者一个接口实现;在具体的情况下也不排除使用具体类实现。
具体主题(ConcreteSubject)角色
保存对具体观察者对象有用的内部状态;在这种内部状态改变时给其观察者发出一个通知;具体主题角色又叫作具体被观察者角色;
具体观察者(ConcreteObserver)角色
保存一个指向具体主题对象的引用;和一个与主题的状态相符的状态。具体观察者角色实现抽象观察者角色所要求的更新自己的接口,以便使本身的状态与主题的状态自恰。
具体观察者角色,通常用一个具体子类实现。

4 Observer机制的实现

4.1 Observer机制的具体分析:

1) Subject(主题)需要为注册和注销观察者分别提供一个接口(如register, unRegister接口)。
2) Observer(观察者)需要提供一个可以从Subject接受通知的接口(如update接口)。
Subject在状态发生变化时,利用改接口通知相应的Observer。
3) 下面的两点也需要满足:
(1) 拉模型(In the pull model)--主题需要提供一个接口,可以使观察者查询主题获得需要的状态信息来更新自己的状态。
(2) 推模型(In the push model)--主题发送观察者可能关注的状态信息。

基于以上分析,我们提供两个接口,Observable接口和Observer接口。
Observable接口:提供regiseter(),unRegister()和Notify()三个接口。Subject类都需要实现该接口。
Observer接口:提供synchronizeState()接口。Observer类需实现该接口。

下图说明各类之间的相互关系。



图1 主题及观察者类交互图

4.2 Observer模式实例

我们实现一车辆信心管理系统,来对单位各部门的车辆信息进行管理。用户选择部门名称,显示该部门的车辆列表以及该部门的年度车辆费用报表。系统界面示意图如下所示:




图2 车辆信息管理系统示意图

客户端交互流程:
1) 用户选择部门列表,以查看部门车辆信息。
2) 用户选择部门列表后,系统显示该部门的车辆列表和该部门的年度车辆费用两组信息。
3) 用户选择其他部门,两组信息会同时在客户端刷新。

由客户端交互流程可知,两组车辆信息更新依赖于部门信息列表变化。可以想象的出,交互过程是一个拥有部门信息列表的对象,一个管理车辆列表信息显示的对象和一个管理年度车辆费用显示的对象三个对象之间的交互。
该系统的类设计参考图1,各类交互图可以如下图所示:




图3 车辆信息管理系统类交互图

具体类及接口定义如下:

1)Observable接口:

public interface Observable {

 public void notifyObservers();

 public void register(Observer obs);

 public void unRegister(Observer obs);

}


2)CarInfoManager类实现Observable接口,并通过register和unRegister接口使观察着对象(此处为CarListInfo和CarCostInfo的实例)注册为自己的观察者,具体如下。

public class CarInfoManager  implements Observable {

         private Vector observersList;

         private String deptName;

  public CarInfoManager () throws Exception {

…  

observersList = new Vector();

}

public void register(Observer obs) {

  //动态观察者链表中添加观察者

  observersList.addElement(obs);

}

public void unRegister(Observer obs) {

  //观察者链表中删除观察者

       //observersList.removeElement(obs);

}

public void notifyObservers() {

  //向所有观察者发送通知

  for (int i = 0; i < observersList.size(); i++) {

            Observer observer = (Observer) observersList.elementAt(i);

         observer.update(this);

}

}

public String getDeptName() {

  return deptName;

}

public void setDeptName (String dept) {

  DeptName = dept;

       NotifyObservers(); //此处向观察者发送通知

}
</pre><p></p><p style="color:rgb(51,51,51)"><span lang="EN-US" style="color:rgb(51,51,51); font-family:Arial; font-size:14px; line-height:26px">3</span><span style="color:rgb(51,51,51); font-size:14px; line-height:26px; font-family:宋体">)</span><span lang="EN-US" style="color:rgb(51,51,51); font-family:Arial; font-size:14px; line-height:26px">Observer </span><span style="color:rgb(51,51,51); font-size:14px; line-height:26px; font-family:宋体">接口:</span></p><p style="color:rgb(51,51,51)"><span style="color:rgb(51,51,51); font-size:14px; line-height:26px; font-family:宋体"></span></p><p style="color:rgb(51,51,51)"><span style="color:rgb(51,51,51); font-size:14px; line-height:26px; font-family:宋体"></span></p><pre name="code" class="java">public interface Observer {  

public void update(Observable subject);

}
4)CarListInfo类:实现Observer接口。

class CarListInfo implements Observer {

       private CarInfoManager iManager;

 

public CarListInfo() {

              iManager = NULL;

}

 

       public CarListInfo(CarInfoManager aManager) {

              iManager = aManager;

              iManager.register(this);

}

 

public update(Observable subject){      //显示车辆列表信息

       if(subject == iManager) {

              //收到通知,获取新状态,更新显示信息

              String deptName = iManager.getDeptName();

              //更新车辆列表信息代码

              //…

}

}

public void setManager(CarInfoManager aManager)

{

              iManager = aManager;

       iManager.register(this);

}

}


5)CarCostInfo类:实现Observer接口。

class CarCostInfoimplements Observer {

       private CarInfoManager iManager;

public CarCostInfo () {

              iManager = NULL;

}

       public CarCostInfo (CarInfoManager aManager) {

              iManager = aManager;

              iManager.register(this);

}

 

public update(Observable subject){      //显示车辆费用信息

       if(subject == iManager) {

              //收到通知,获取新状态,更新显示信息

              String deptName = iManager.getDeptName();

              //更新车辆费用信息代码

              //…

}

}

public void setManager(CarInfoManager aManager)

{

              iManager = aManager;

       iManager.register(this);

}

}


客户端程序代码如下:

public class SupervisorView {

       …

  public static void main(String[] args) throws Exception {

//创建主题对象

CarInfoManager iSubject = new CarInfoManager ();

//创建观察者

CarCostInfo costOb = new CarCostInfo(iSubject);

CarListInfo listOb = new CarListInfo (iSubject);

 

//主题状态更新、通知各观察者

iSubject.setDeptName(“财务部”);

iSubject.setDeptName(“技术部”);

}

}


5 Observer模式在Java中的应用

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

在Java语言的java.util库里面,提供了一个Observable类以及一个Observer接口(Java.util.Observer/ Java.util.Observable),构成Java语言对观察者模式的支持。我们只需要直接实现他们就可以。
Observer接口
  这个接口只定义了一个方法,update()。当被观察者对象的状态发生变化时,这个方法就会被调用。这个方法的实现应当调用每一个被观察者对象的notifyObservers()方法,从而通知所有的观察对象。

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的子对象并不知道观察者对象一直在观察着它们。

5.2 实例分析

我们还是拿上一节车辆信息管理系统来举例:
具体类设计如下:
1)CarInfoManager类实现Observable接口,并通过register和unRegister接口使观察着对象(此处为CarListInfo和CarCostInfo的实例)注册为自己的观察者,具体如下。

public class CarInfoManager  implements Observable {

         private Vector observersList;

         private String deptName;

  public CarInfoManager () throws Exception {

…  

observersList = new Vector();

}

public String getDeptName() {

  return deptName;

}

public void setDeptName (String dept) {

  DeptName = dept;

setChanged();

       NotifyObservers(this);   //此处向观察者发送通知

}


2)CarListInfo类:实现Observer接口。

class CarListInfo implements Observer {

       private CarInfoManager iManager;

 

public CarListInfo() {

              iManager = NULL;

}

 

       public CarListInfo(CarInfoManager aManager) {

              iManager = aManager;

              iManager. addObserver (this);  //注意addObserver与上节中register名称变化。

}

 

public update(Observable obj, Object arg){  //显示车辆列表信息

       if(obj == iManager) {

              //收到通知,获取新状态,更新显示信息

              String deptName = (String)arg;

              //更新车辆列表信息代码

              //…

}

}

public void setManager(CarInfoManager aManager)

{

              iManager = aManager;

       iManager. addObserver (this);

}

}

3)CarCostInfo类:实现Observer接口。

class CarCostInfoimplements Observer {

       private CarInfoManager iManager;

 

public CarCostInfo () {

              iManager = NULL;

}

 

       public CarCostInfo (CarInfoManager aManager) {

              iManager = aManager;

              iManager.addObserver(this);

}

 

public update(Observable obj, Object arg){  //显示车辆费用信息

       if(obj == iManager) {

              //收到通知,获取新状态,更新显示信息

              String deptName = (String)arg;

              //更新车辆列表信息代码

              //…

}

}

public void setManager(CarInfoManager aManager)

{

              iManager = aManager;

       iManager. addObserver (this);

}

}


总上可知,利用Java API提供的Observable和Observer接口可以在系统中快速的应用观察者模式。

6 总结

6.1 观察者模式的效果

观察者模式的效果有以下的优点
第一、观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。
由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。如果被观察者和观察者都被扔到一起,那么这个对象必然跨越抽象化和具体化层次。
第二、观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知。

观察者模式有下面的缺点
第一、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
第二、如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。
第三、如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
第四、虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的,仅仅知道发生了什么变化而已。

6.2 观察者模式与其它模式的关系

  观察者模式使用了备忘录模式(Memento Pattern)暂时将观察者对象存储在被观察者对象里面。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: