Observer模式及其在JSF中的应用

 
一. 简介

Observer(观察者)模式是比较常用的一个模式,尤其在界面设计中应用广泛

Observer模式的一般意图是:将对象内部状态的改变自动通知给对此感兴趣的相关对象。《设计模式》一书中对Observer模式的意图是这样叙述的:定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知并自动更新。

二. 示例
    示例一(电子商务应用)
     1. 应用场景

         在编写应用程序的过程中,假设来了一个新需求:只要有一个新消费者进入系统时,需要进行以下操作:

向消费者发送一封欢迎邮件(1

向邮局查证消费者的地址2

如果这就是所有需求的话,那么将通知行为硬编码到customer类中,硬编码的方式就能能奏效。

 

但是需求在变化。若又有一个需求,将再次要求customer类的行为改变。如:要给消费者发送一封优惠券(3。如果硬编码的话,customer又要变化。这样明显不够灵活,不符合OCP设计原则。(OCP(Open-Closed Principle)的核心含意是:一个好的设计应该能够容纳新的功能需求的增加,但是增加的方式不是通过修改现有的模块(类),而是通过增加新的模块(类)来完成的。如果一个设计能够遵循OCP,那么就能够有效解决系统的通用性和扩展性。为了使设计能够符合OCP原则,就要求我们在进行设计时不能简单的以功能为核心,而是以通用性的抽象为核心。

 

2 场景分析

 

这个案例中:

A不同类型的对象-----有一系列对象(如(1)、(2)、(3))需要在状态变化时获得通知,这些对象往往属于不同的类

B不同的接口-----因为它们(如(1)、(2)、(3))属于不同的类,所以往往有不同的接口

3 采用observer模式

A 找出所有希望获得通知的对象,这些对象为观察者,因为它们在观察一个事件的发生。如果观察者的类型都相同,目标(触发事件的对象subject)就可以轻易地通知它们。如果没有相同的接口,就必须修改目标,以处理各种不同类型的观察者。

所以,第一步,让观察者以同样的方式工作。在java中,可以用一个接口。

 

B 观察者需要负责了解自己观察的是什么,而且目标无需知道有哪些观察者依赖自己。为此,观察者需要有一种方式向目标注册。观察者都是相同类型的,所以在目标中添加两个方法:attach(Observer),---将给定的observer添加到目标的观察者列表中;detach(Observer)---从目标的observer列表中删除给定的观察者对象。

所以,第二步,让观察者注册自己

 

C 事件发生时,目标对象通知observer对象非常简单,每个observer都要实现一个名为update的方法。目标类将实现一个notify方法来遍历其observer对象列表,并调用每个observer对象的update方法,update方法包含处理事件的代码。

然而,只是通知每个observer对象并不够,observer对象除了知道事件发生之外,可能还需要关于该事件的更多的信息,因为必须在subject类添加方法(setState(),getState()),使observer对象能够获取所需的任何信息。

所以第三步,事情发生时通知观察者

各个类的关系如下:

A Observer对象在实例化时将自己添加到Customer类。如果Observer对象需要从目标(customer类)中获得更多信息,就必须传给update方法一个指向调用对象的引用。

 

B 当添加一个新的customer对象时,notify方法将调用Observer对象。

4 Java代码片段

 
import java.util.*;
 
public class Customer {
      private Vector myObs = new Vector();
     
      /*以下两个方法使用了态方法,因为观察者希望得到所有Customer对象的通知。知观察者时,将传给它们新建的Customer对象的引用  */
      public static void attach(MyObserver o) {
            myObs.addElement(o);
      }
     
      public static void detach(MyObserver o) {
          myObs.remove(o);
      }
     
      //可以设置参数以便通知observers发生了什么事
      public void notifyObs() {
                 for (Enumeration e = myObs.elements(); e.hasMoreElements();) {
                      ((MyObserver)e).update(this);
                 }
      }
     
      interface MyObserver {
           void update(Customer myCust);
      }
     
      class AddrVerification implements MyObserver {
           public AddrVerification() {
           }
           public void update(Customer myCust) {
                 //具体实现:如查证消费者住址的员工可能通过myCust获取customer的更多信息
           }
      }
     
      class WelcomeLetter implements MyObserver {
           public WelcomeLetter () {
           }
           public void update(Customer myCust) {
                 //具体实现:如负责发欢迎邮件的员工可能通过myCust获取customer的更多信息
           }
      }
}

5 扩展

如果有新的需求,公需添加一具新的观察者来发送优惠券.解决方案如下图

示例二(网上商店中的应用)

1 应用场景

虽然网上商店形式多样,每个站点有自己的特色,但也有其一般的共性,单就"商品的变化,以便及时通知订户"这一点,是很多网上商店共有的模式,这一模式类似Observer patern观察者模式.具体的说,如果网上商店中商品在名称、价格等方面有变化,如果系统能自动通知会员,将是网上商店区别传统商店的一大特色.这就需要在商品product中加入Observer这样角色,以便product细节发生变化时,Observer能自动观察到这种变化,并能进行及时的update动作.

2 java代码

Observer工程中的文件结构图如下所示。

Observer工程中的文件结构 

 

    A. Product.java:

package   subject;

import  java.util.Vector;
import  observers.Observers;

public   class  Product  {
    
private String name;
    
private float price;
    
private Vector obs;
    
    
boolean changed = false;
    
    
public Product() {
        obs 
= new Vector();
    }

    
//此处synchronized的用法:即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是执行attach方法的线程)执行完该方法后,别的线程才能进入.
    public synchronized void attach(Observers obser) {
        
if(obser == null)
            
throw new NullPointerException();
        
if (!obs.contains(obser))
            obs.add(obser);
    }

    
public synchronized void detach(Observers obser) {
        obs.removeElement(obser);
    }

    
public synchronized boolean hasChanged() {
        
return changed;
    }

    
public void clearChanged() {
        changed 
= false;
    }

    
    
protected synchronized void setChanged() {
        changed 
= true;
    }

    
    
public void notifyObservers(Object arg) {
        Object[] observer;
        
        
/* 参见java.util.Observable中此处:
         * We don't want the Observer doing callbacks into
         * arbitrary code while holding its own Monitor.
         * The code where we extract each Observable from 
         * the Vector and store the state of the Observer
         * needs synchronization, but notifying observers
         * does not (should not).  The worst result of any 
         * potential race-condition here is that:
         * 1) a newly-added Observer will miss a
         *   notification in progress
         * 2) a recently unregistered Observer will be
         *   wrongly notified when it doesn't care
         
*/
        
        
synchronized (this{
            
if(!changed)
                
return;
            observer 
= obs.toArray();
            clearChanged();
        }

        
for(int i = observer.length-1; i >= 0; i--{
            ((Observers) observer[i]).update(
this,arg);
        }

    }

    
    
public String getName() {
        
return name;
    }

    
public void setName(String name) {
        
this.name = name;
        setChanged();
        notifyObservers(name);
        
    }

    
public float getPrice() {
        
return price;
    }

    
public void setPrice(float price) {
        
this.price = price;
        setChanged();
        notifyObservers(price);
    }

}

      B.  Observers.java:
package  observers;

public  interface  Observers  {
    
void update(Object obj, Object arg);
}

      C. ObserverName.java:

package  observers;

public   class  ObserverName  implements  Observers  {

    
public void update(Object obj, Object arg) {
        
// TODO Auto-generated method stub
        if(arg instanceof String) {
            String name 
= arg.toString();
            System.out.println(
"ObserverName: name change to " + name);
        }

    }

}

       D.  ObserverPrice.java:

package  observers;

public   class  ObserverPrice  implements  Observers {

    
public void update(Object obj, Object arg) {
        
// TODO Auto-generated method stub
        if(arg instanceof Float) {
            
float price = ((Float)arg).floatValue();
            System.out.println(
"ObserverPrice: the price is changed to "+ price);
        }

    }

}

      E.   ObserverTest.java:

package  test;

import  observers.ObserverName;
import  observers.ObserverPrice;
import  subject.Product;

public   class  ObserverTest  {
    
public static void main(String[] arg){
        Product product 
= new Product();
        ObserverName obsName 
= new ObserverName();
        ObserverPrice obsPrice 
= new ObserverPrice();
        
        product.attach(obsName);
        product.attach(obsPrice);
        
        product.setName(
"icecreamchen");
        product.setPrice(
2.26f);
    }

}

3.    运行结果

ObserverName: name change to icecreamchen
  ObserverPrice: the price is changed to 
2.26

三. 此模式在JSF中用使用

JSF提供了两种类型的事件:数值改变(Value Changed)Action 数值改变事件(javax.faces.event.ValueChangeEvent)用于观察用户界面组件property的改变(如修改文本框的内容);Action事件(javax.faces.event.ActionEvent)则对观察由UICommand派生的用户界面组件(包括按钮和超链接)的激活动作非常有用。所用这些事件类型最终都从JSF中的共同祖先 javax.faces.event.FacesEvent继承而来。

JSF中基本上有两个步骤来捕获事件:

第一步:为你希望接收事件的组件实现适合的侦听器接口。

a.在实现ValueChangeListener接口时,需要添加processValueChanged()方法,在此方法中实现响应事件的代码;

例如:

<h:inputText id="userId" value="#{login.userId}">

    <f:valueChangeListener  type="logindemo.UserLoginchanged">

</h:inputText>

当用户改变了该输入框并最终提交了这些改动,UserLoginchanged类将实现ValueChangeListene的接口 ,并调用其实例的processValueChanged()方法

b.在实现ActionListener接口时,需要添加processAction()方法。

例如:

<h:commandButton id="login" value="login">

    <f:actionListener type="logindemo.LoginActionListener">

</h:commandButton>

其中LoginActionListener会实现实现ActionListener接口,调用processAction()方法。

另外,处理ActionEvent还可以直接为该命令按钮注册一个Action处理器。

<h:commandButton immediate="true" id="goodbye" action="#{helloBean.goodbye}" value="goodbye"></h:commandButton>

 

注意:如果希望动态改变对事件的响应,注册Action事件侦听器更合适
 
第二步 注册你新建的侦听器

 

四. 参考资料

              

         1.设计模式解析(Design Patterns Explained Alan Shalloway, James R.Trott
         2Mastering JavaServer Faces 中文版
         3. 设计模式之Observer板桥里人 http://www.jdon.com 
     
     
      
      2002/3/16
     
     

 

     

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值