网管联盟 | 网管论坛 | 网管u家 | 网管博客 | 网管软件 | 网管求职 | 小游戏 | 网管搜索 | 网管原创 | 网管聚合 | 网管读摘 | 网管焦点 | 世界素材 | 会员投稿 | 会员中心 
中国网管联盟
Windows Linux Cisco 网络技术 数据库 黑客攻防 DotNet Java PHP 认证 新闻资讯 服务器 存储资讯 网络设备 网管学堂 技术专题 焦点 网吧频道
 当前位置: > bitsCN.com > JAVA > 高级技术 > 设计模式 > 观察者模式组图(Observer Pattern)  

观察者模式组图(Observer Pattern)

2005-12-01  作者:BitsCN整理  来源:中国网管联盟  点评 投稿 收藏


  描述:
  
  在设计一组依赖的对象与它们所依赖的对象之间一致(同步)的交流模型时,观察者模式(Observer Pattern)很有用。它可以使依赖对象的状态与它们所依赖的对象的状态保持同步。这组依赖的对象指的是观察者(Observer),它们所依赖的对象称为主题(Subject)。为了实现观察者(Observer)的状态与主题(Subject)保持同步,观察者模式(Observer Pattern)
  
  推荐采用发布者--订阅者(publisher--subscriber)模型,以使这组观察者(Observer)和主题(Subject)对象之间有清晰的界限。
  
  典型的观察者(Observer)是一个依赖于或者关注于主题对象的状态的对象。一个主题可以有一个或者多个观察者。这些观察者在主体的状态发生变化时,需要得到通知。
  
  由于给定主体的观察者链表需要动态的变化,因此一个主题不能维护一个静态的观察者链表。因此关注于主题状态的任何对象都需要明确地注册自己为主体的一个观察者。主题状态发生的变化,都需要通知所有的以注册的观察者。从主题接到通知以后,每一个观察者查询主题,使自己的状态与主题的同步。因此一个主题扮演着发布者的角色,发布信息到所有的以订阅的观察者。
  
  换句话说,主题和它的观察者之间包含了一对多的关系。当主题的实例的状态发生变化时,所有的依赖于它的观察者都会得到通知并更新自己。每一个观察者对象需要向主题注册,当主题的状态发生变化的时候得到通知。一个观察者可以注册或者订阅多个主题。当观察者不希望再得到通知时,它可以向主题进行注销。 中国网管联盟bitsCN.com
  
  为了实现这种机制:
  
  (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接口的实现。
  
  在应用观察者模式时,有几种变体。这就会产生不同类型的主题--观察者模式,例如,观察者仅关注主体特定类型的变化等。
  
  增加新的观察者:
  

网管网www.bitscn.com


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

  public void register(Observer obs);
  public void unRegister(Observer obs);
  }
  
  ReportManager类(Listing33.1)提供了声明在Observable接口中方法的实现。两个依赖于ReportManager的报表对象使用这些方法注册它们自己为观察者。ReportManager把这些注册的观察者保存到observersList矢量(vector)中。当前选择的分类构成了ReportManager对象的状态,它以实例变量的形式保存在变量department中。当为department设置一个新的值时(也就是ReportManager对象的状态改变),notifyObservers方法被调用。作为notifyObservers方法的一部分,ReportManager调用注册为观察者的refreshData(Observable)方法。
  Listing 33.1: ReportManager Class
  
  public class ReportManager extends JFrame
  implements Observable {
  …
  …
  private Vector observersList;
  private String department;
  public ReportManager() throws Exception {
  …
  …
  observersList = new Vector();
  …
  …
  }
  public void register(Observer obs) {
  //Add to the list of Observers

中国网管联盟bitsCN.com


  observersList.addElement(obs);
  }
  public void unRegister(Observer obs) {
  //remove from the list of Observers
  }
  public void notifyObservers() {
  //Send notify to all Observers
  for (int i = 0; i < observersList.size(); i++) {
  Observer observer =
  (Observer) observersList.elementAt(i);
  observer.refreshData(this);
  }
  }
  public String getDepartment() {
  return department;
  }
  public void setDepartment(String dept) {
  department = dept;
  }
  class ButtonHandler implements ActionListener {
  ReportManager subject;
  public void actionPerformed(ActionEvent e) {
  if (e.getActionCommand().equals(ReportManager.EXIT)) {
  System.exit(1);
  }
  if (e.getActionCommand().equals(ReportManager.SET_OK)) {
  String dept = (String)
  cmbDepartmentList.getSelectedItem();
  //change in state
  subject.setDepartment(dept);
  subject.notifyObservers();
网管网www_bitscn_com

  }
  }
  public ButtonHandler() {
  }
  public ButtonHandler(ReportManager manager) {
  subject = manager;
  }
  }
  }//end of class
  
  除了提供Observable接口方法的实现,ReportManager还显示了必要的用户接口,允许用户选择一个特定的、关注的分类。
  
  让我们定义接口Observer的两个实现:MonthlyReport和YTDChart类
  
  public interface Observer {
  public void refreshData(Observable subject);
  }
  
 
  Figure 33.2: Observer Class Hierarchy
  

  Listing 33.2: MonthlyReport Class as an Observer
  
  public class MonthlyReport extends JFrame implements Observer {
  …
  …
  private ReportManager objReportManager;
  public MonthlyReport(ReportManager inp_objReportManager)
  throws Exception {
  super("Observer Pattern -- Example");
  objReportManager = inp_objReportManager;
  //Create controls
  …

中国网管论坛bbs.bitsCN.com


  …
  //Create Labels
  …
  …
  objReportManager.register(this);
  }
  public void refreshData(Observable subject) {
  if (subject == objReportManager) {
  //get subject's state
  String department = objReportManager.getDepartment();
  lblTransactions.setText(
  "Current Month Transactions - " +
  department);
  Vector trnList =
  getCurrentMonthTransactions(department);
  String content = "";
  for (int i = 0; i < trnList.size(); i++) {
  content = content +
  trnList.elementAt(i).toString() + "\n";
  }
  taTransactions.setText(content);
  }
  }
  private Vector getCurrentMonthTransactions(String department
  ) {
  Vector v = new Vector();
  FileUtil futil = new FileUtil();
  Vector allRows = futil.fileToVector("Transactions.date");
  //current month
  Calendar cal = Calendar.getInstance();
  cal.setTime(new Date());
  int month = cal.get(Calendar.MONTH) + 1; 网管bitscn_com
  String searchStr = department + "," + month + ",";
  int j = 1;
  for (int i = 0; i < allRows.size(); i++) {
  String str = (String) allRows.elementAt(i);
  if (str.indexOf(searchStr) > ?1) {
  StringTokenizer st =
  new StringTokenizer(str, ",");
  st.nextToken();//bypass the department
  str = " " + j + ". " + st.nextToken() + "/" +
  st.nextToken() + "~~~" +
  st.nextToken() + "Items" + "~~~" +
  st.nextToken() + " Dollars";
  j++;
  v.addElement(str);
  }
  }
  return v;
  }
  }//end of class
  
  ReportManager利用这个接口通知它的所有观察者。
  
  主题--观察者的关联(Subject--Observer Association)
  
  通常,一个客户首先需要创建一个主题(ReportManager)实例,当一个观察者(例如:MonthlyReport,YTDChart)对象被创建。客户把主题ReportManager实例的引用传递给观察者的构造函数,观察者将自身注册到当前主题实例上。
  
  //Client Code
  public class SupervisorView {
  …

中国网管论坛bbs.bitsCN.com


  …
  public static void main(String[] args) throws Exception {
  //Create the Subject
  ReportManager objSubject = new ReportManager();
  //Create Observers
  new MonthlyReport(objSubject);
  new YTDChart(objSubject);
  }
  }//end of class
  
  类之间的关联描述如下:
  
 
  Figure 33.3: Example Application--Class Association
  

  逻辑流程:
  
  (1)  使用ReportManager用户接口,当用户选择一个特定的分类并且点击OK按钮时,ReportManager的内部状态被被改变(例如,ReportManager实例变量department的值发生改变)。
  
  (2)  新的状态一旦被设置,ReportManager调用两个注册的观察者MonthlyReport和YTDChart的refreshData(Observable)方法。
  
  (3)  作为refreshData方法的一部分,两个report对象需要:
  
  A、  检查以确保调用refreshData方法的主题和观察者这册的主题是同一个主题。这就避免了观察者响应不必要的调用。
  
  B、  使用getDepartment方法查询ReportManager的当前状态。 网管下载dl.bitscn.com
  
  C、  从数据文件中提取响应的数据显示。
  
 
  Figure 33.4: MonthlyReport View

  
  当ReportManager的状态变化逻辑实现需要改变时,任何观察者不受影响。同样,当一个新的观察者被加入时,ReportManager类不需要任何变化。

TAGs组图   模式   观察者   主题   状态   对象   ReportManager    
 上一篇:备忘录模式(Memento Pattern)   下一篇:深入浅出Java设计模式之迭代器模式(图)
相关文章列表
观察者模式组图(Observer Pattern) 评论:
loading.. 评论加载中…
评论:请自觉遵守互联网相关政策法规,评论不得超过250字。

验证码: 注册用户
本类热门排行:
1.Java对各种文件的操作详解
2.JAVA设计模式之事务处理
3.实现Java抽象工厂模式
4.两种Java容器类List和Set分析
5.Java实时多任务调度过程中的安全监控设计
6.开发JAVA编程中字符串分割的两种方法
7.教程:适配器模式(Adapter Pattern)组图
8.领域模型驱动设计(DDD)之模型提炼
9.JAVA操作数据库方式与设计模式应用
10.Hibernate一对多单向关系
最新推荐文章:
1.JMX心得
2.设计模式一些思索
3.Java 学习笔记:泛型(一)
4.J2SE----集合框架
5.自己写框架
6.设计模式之Singleton(单态模式)
7.25种java模式代码参考手册
8.设计模式之创建模式
9.EMF模型解析的策略分析
10.实现Java抽象工厂模式
网管论坛交流:
·不疯魔不成活
·令你大开眼界的真正标准化机房,已整理重
·为赈灾,女孩舍身拍“裸照”
·Windows Server 2003服务器群集创建和配
·exchange2k3全套官方资料
·双儿一周岁了。。。特殊的礼物来啦。。
·存储备份技术版块守则
·无盘技术交流区守则
·DOS命令基础大全之命令详解<作者吐血
·Windows XP 操作系统默认设置需要注意的