Java资源网

| JAVA基础 | 环境配置 | JDBC | 线程技术 | Socket编程 | JavaMail | JAVA与XML | 设计模式 | 技术新闻 | Java认证 | 程序人生 软件下载
| JSP&Servlet | Spring | Struts | Hibernate | JBuilder | Eclipse | WebService | EJB技术 | J2ME开发 | 应用服务器 | JXTA | Ajax
Articles search文章搜索
   关键字:
   类 别:
       
New download 最新下载
· [组件]HTML Parser 1.5
· [教程]WebSphere Studio应用教程
· [组件]JDom 1.0
· [工具]Junit3.8.1
· [教程]EJB编程及J2EE系统架构和设计
· [教程]EJB教程
· [教程]J2EE Tutorial中文版
· [教程]Java编程思想2(英文)
· [教程]java编程思想(完整版)
· [教程]Java网络编程
New articles 最新文章
· 设计移动 Web 服务
· 解析XML的时候完全忽略DTD
· 理解XML Schema XML Schema 初步
· 标签库的深入研究
· 提升JSP应用程序的七大绝招
· 如何使用JDOM对XML文件进行操作
· 处理XML字符串中特殊字符
· 利用Digester把XML转换成为Java对象
· 使用WebService 和RMI远程协作
· 使用Axis开发Web Service程序
Articles top 热门文章
· Eclipse基础--plugin插件安装(6644)
· eclipse+tomcat+lomboz的安装配置说明(4774)
· Java程序员就业前景(4584)
· Windows下JAVA环境变量的设置祥解(3788)
· Tomcat下JSP、Servlet和JavaBean环境的配置(3716)
· 使用links方式安装Eclipse插件(3698)
· 一个老程序员的心理话(3533)
· linux下jdk的安装与配置(3459)
· 初学者入门:Structs中基本配置入门(3334)
· Eclipse 运行命令行参数大全(3084)
您的位置:首页>>设计模式>>观察者模式(Observernbsp;Pattern)
观察者模式(Observernbsp;Pattern)
2007-04-13   来源:www.javaresearch.org  作者:未知

第33章 观察者模式(Observer Pattern)


描述:


    在设计一组依赖的对象与它们所依赖的对象之间一致(同步)的交流模型时,观察者模式(Observer Pattern)很有用。它可以使依赖对象的状态与它们所依赖的对象的状态保持同步。这组依赖的对象指的是观察者(Observer),它们所依赖的对象称为主题(Subject)。为了实现观察者(Observer)的状态与主题(Subject)保持同步,观察者模式(Observer Pattern)
推荐采用发布者--订阅者(publisher--subscriber)模型,以使这组观察者(Observer)和主题(Subject)对象之间有清晰的界限。
    典型的观察者(Observer)是一个依赖于或者关注于主题对象的状态的对象。一个主题可以有一个或者多个观察者。这些观察者在主体的状态发生变化时,需要得到通知。
    由于给定主体的观察者链表需要动态的变化,因此一个主题不能维护一个静态的观察者链表。因此关注于主题状态的任何对象都需要明确地注册自己为主体的一个观察者。主题状态发生的变化,都需要通知所有的以注册的观察者。从主题接到通知以后,每一个观察者查询主题,使自己的状态与主题的同步。因此一个主题扮演着发布者的角色,发布信息到所有的以订阅的观察者。
    换句话说,主题和它的观察者之间包含了一对多的关系。当主题的实例的状态发生变化时,所有的依赖于它的观察者都会得到通知并更新自己。每一个观察者对象需要向主题注册,当主题的状态发生变化的时候得到通知。一个观察者可以注册或者订阅多个主题。当观察者不希望再得到通知时,它可以向主题进行注销。
    为了实现这种机制:
(1)    主题需要为注册和注销通知提供一个接口。
(2)    下面的两点也需要满足:
A、    拉模型(In the pull model)--主题需要提供一个接口,可以使观察者查询主题获得需要的状态信息来更新自己的状态。
B、    推模型(In the push model)--主题发送观察者可能关注的状态信息。
(3)    观察者需要提供一个可以从主题接受通知的接口。
类图(图33.1)描述了为满足于以上需求,不同类的结构和它们之间的关联关系。
  
Figure 33.1: Generic Class Association When the Observer Pattern Is Applied 
从这个类图可以看到:
(1)    所有的主题需要提供一个类似于Observable接口的实现。
(2)    所有的观察者需要提供一个类似于Observer接口的实现。
在应用观察者模式时,有几种变体。这就会产生不同类型的主题--观察者模式,例如,
观察者仅关注主体特定类型的变化等。
增加新的观察者:

    应用观察者模式以后,在不影响主题类的情况下,可以动态的加入不同的观察者。同样,主题的状态变化逻辑改变时,观察者也不会受到影响。
例子:
    为了管理一个卖厂多个分类产品,让我们建立一个销售报表系统。这个系统有以下特征:
(1)    用户可以选择一个他们感兴趣的分类
(2)    在选择了一个分类以后,需要显示下面的两种类型的报表。
A、    月度报表(Monthly report)--所选分类当月的所有交易清单。
B、    年度累积额(YTD sales chart)--以月为单位显示选择分类的年度累积额图。
(3)    当一个不同的分类被选择时,两种报表的数据会被刷新,显示当前所选分类的报表。
为了实现以上期望的功能,我们很容易的看到两个报表对象依赖于持有用户选择分类的对象。应用观察者模式于此场景,我们可以设计一个介于持有用户选择分类的对象和两个报表对象之间一个一致(同步)的交流模型。
让我们定义三个类,它们的功能如表33.1所描述:
Table 33.1: Subject-Observer Classes 
 
  1. public interface Observable { 
  2.   public void notifyObservers(); 
  3.   public void register(Observer obs); 
  4.   public void unRegister(Observer obs); 

  
Figure 33.2: Observable Interface and Its Implementer 
ReportManager类(Listing33.1)提供了声明在Observable接口中方法的实现。两个依赖于ReportManager的报表对象使用这些方法注册它们自己为观察者。ReportManager把这些注册的观察者保存到observersList矢量(vector)中。当前选择的分类构成了ReportManager对象的状态,它以实例变量的形式保存在变量department中。当为department设置一个新的值时(也就是ReportManager对象的状态改变),notifyObservers方法被调用。作为notifyObservers方法的一部分,ReportManager调用注册为观察者的refreshData(Observable)方法。
Listing 33.1: ReportManager Class 
  1. public class ReportManager extends JFrame 
  2.   implements Observable { 
  3.           … 
  4.           … 
  5.   private Vector observersList; 
  6.   private String department; 
  7.   public ReportManager() throws Exception { 
  8.           … 
  9.           … 
  10.     observersList = new Vector(); 
  11.           … 
  12.           … 
  13.   } 
  14.   public void register(Observer obs) { 
  15.     //Add to the list of Observers 
  16.     observersList.addElement(obs); 
  17.   } 
  18.   public void unRegister(Observer obs) { 
  19.     //remove from the list of Observers 
  20.   } 
  21.   public void notifyObservers() { 
  22.     //Send notify to all Observers 
  23.     for (int i = 0; i < observersList.size(); i++) { 
  24.      Observer observer = 
  25.        (Observer) observersList.elementAt(i); 
  26.      observer.refreshData(this); 
  27.     } 
  28.   } 
  29.   public String getDepartment() { 
  30.     return department; 
  31.   } 
  32.   public void setDepartment(String dept) { 
  33.     department = dept; 
  34.   } 
  35.    class ButtonHandler implements ActionListener { 
  36.      ReportManager subject; 
  37.      public void actionPerformed(ActionEvent e) { 
  38.        if (e.getActionCommand().equals(ReportManager.EXIT)) { 
  39.         System.exit(1); 
  40.        } 
  41.        if (e.getActionCommand().equals(ReportManager.SET_OK)) { 
  42.         String dept = (String
  43.                      cmbDepartmentList.getSelectedItem(); 
  44.         //change in state 
  45.         subject.setDepartment(dept); 
  46.         subject.notifyObservers(); 
  47.        } 
  48.      } 
  49.      public ButtonHandler() { 
  50.      } 
  51.      public ButtonHandler(ReportManager manager) { 
  52.        subject = manager; 
  53.      } 
  54.   } 
  55. }//end of class 

    除了提供Observable接口方法的实现,ReportManager还显示了必要的用户接口,如图33.3所示,允许用户选择一个特定的、关注的分类。
  
Figure 33.3: ReportManager User Interface 
让我们定义接口Observer的两个实现:MonthlyReport和YTDChart类(如图:33.4和Listing33.2)
  1. public interface Observer { 
  2.   public void refreshData(Observable subject); 
 
 
Figure 33.4: Observer Class Hierarchy 
Listing 33.2: MonthlyReport Class as an Observer 
  1. public class MonthlyReport extends JFrame implements Observer { 
  2.           … 
  3.           … 
  4.   private ReportManager objReportManager; 
  5.   public MonthlyReport(ReportManager inp_objReportManager) 
  6.   throws Exception { 
  7.     super("Observer Pattern -- Example"); 
  8.     objReportManager = inp_objReportManager; 
  9.     //Create controls 
  10.           … 
  11.           … 
  12.     //Create Labels 
  13.           … 
  14.           … 
  15.     objReportManager.register(this); 
  16.   } 
  17.   public void refreshData(Observable subject) { 
  18.     if (subject == objReportManager) { 
  19.      //get subject's state 
  20.      String department = objReportManager.getDepartment(); 
  21.      lblTransactions.setText( 
  22.        "Current Month Transactions - " + 
  23.        department); 
  24.      Vector trnList = 
  25.        getCurrentMonthTransactions(department); 
  26.      String content = ""
  27.      for (int i = 0; i < trnList.size(); i++) { 
  28.        content = content + 
  29.                  trnList.elementAt(i).toString() + "\n"
  30.      } 
  31.      taTransactions.setText(content); 
  32.     } 
  33.   } 
  34.   private Vector getCurrentMonthTransactions(String department 
  35.                          ) {
  36.     Vector v = new Vector(); 
  37.     FileUtil futil = new FileUtil(); 
  38.     Vector allRows = futil.fileToVector("Transactions.date"); 
  39.     //current month 
  40.     Calendar cal = Calendar.getInstance(); 
  41.     cal.setTime(new Date()); 
  42.     int month = cal.get(Calendar.MONTH) + 1; 
  43.     String searchStr = department + "," + month + ","
  44.     int j = 1; 
  45.     for (int i = 0; i < allRows.size(); i++) { 
  46.      String str = (String) allRows.elementAt(i); 
  47.      if (str.indexOf(searchStr) > ?1) { 
  48.        StringTokenizer st = 
  49.         new StringTokenizer(str, ","); 
  50.        st.nextToken();//bypass the department 
  51.        str = " " + j + ". " + st.nextToken() + "/" + 
  52.              st.nextToken() + "~~~" + 
  53.              st.nextToken() + "Items" + "~~~" + 
  54.              st.nextToken() + " Dollars"
  55.        j++; 
  56.        v.addElement(str); 
  57.      } 
  58.     } 
  59.     return v; 
  60.   } 
  61. }//end of class 

    ReportManager利用这个接口通知它的所有观察者。
主题--观察者的关联(Subject--Observer Association)

    通常,一个客户首先需要创建一个主题(ReportManager)实例,当一个观察者(例如:MonthlyReport,YTDChart)对象被创建。客户把主题ReportManager实例的引用传递给观察者的构造函数,观察者将自身注册到当前主题实例上。
  1. //Client Code 
  2. public class SupervisorView { 
  3.           … 
  4.           … 
  5.   public static void main(String[] args) throws Exception { 
  6.     //Create the Subject 
  7.     ReportManager objSubject = new ReportManager(); 
  8.     //Create Observers 
  9.     new MonthlyReport(objSubject); 
  10.     new YTDChart(objSubject); 
  11.   } 
  12. }//end of class 

类之间的关联描述如下:
  
Figure 33.5: Example Application--Class Association 
逻辑流程:

(1)    使用ReportManager用户接口(图33.3),当用户选择一个特定的分类并且点击OK按钮时,ReportManager的内部状态被被改变(例如,ReportManager实例变量department的值发生改变)。
(2)    新的状态一旦被设置,ReportManager调用两个注册的观察者MonthlyReport和YTDChart的refreshData(Observable)方法。
(3)    作为refreshData方法的一部分,两个report对象需要:
A、    检查以确保调用refreshData方法的主题和观察者这册的主题是同一个主题。这就避免了观察者响应不必要的调用。
B、    使用getDepartment方法查询ReportManager的当前状态。
C、    从数据文件中提取响应的数据显示(如图33.5和33.7)。
 
 
Figure 33.6: MonthlyReport View 
  
Figure 33.7: YTDChart View 
D、    图33.8显示了当应用程序运行时,不同对象之间交互的顺序图。
 
 
Figure 33.8: Application Message Flow 
当ReportManager的状态变化逻辑实现需要改变时,任何观察者不受影响。同样,当一个新的观察者被加入时,ReportManager类不需要任何变化。
附件为本文代码(下载者要支持阿!):

附件:38.rar(17K) [:E][:E][V][V]

  --相关文章--
· 面向对象编程,我的思想 (2007-04-13)
· 面向对象的思维方式 (2007-04-13)
· 通过Javanbsp;Swing看透MVC设计模式 (2007-04-13)
· 适配器模式(Adapternbsp;Pattern) (2007-04-13)
· 追MM与Java的23种设计模式 (2007-04-13)
· 责任链模式(Chainnbsp;ofnbsp;Responsibility) (2007-04-13)

版权所有©2005-2006 JAVA资源网 渝ICP备05007591号 虚拟主机 | 关于我们 | 联系方式 | 广告业务 | 网站地图 | 友情链接