公司的项目告一段落,最近在进行RUP培训项目,有了些空闲的时间。闲暇浏览TheServerSide时,看到一篇名叫《Spring loaded Observer Pattern》的文章,不禁会心一笑——我已在两个月前应用到项目中了。而作者的观点也和我的观点吻合:the Spring framework is a great reason to continue design pattern advocacy in your projects.

    下面我将自己在项目中应用的实例拿出来和大家分享,共同进步。
    我将实际项目中的包名、业务逻辑代码屏蔽掉了,并进行了一定的简化。
    在系统中存在这样的情况:一个功能点触发的动作会引起相关一个或者多个功能点在业务上进行对应的数据处理。而到底有几个功能点要做出相应,要看客户实施了哪些需要作出响应的具体功能点。
    例如:A功能点的某项业务会触发B、C、D三功能点做出回应,而客户购买了哪些功能点是个未知数,也许客户不需要C功能点,对A功能点业务操作作出响应的仅有B、D。
    根据上面的需求可以得出,设计上要尽量的松散耦合,保持各功能点的独立性。观察者模式责无旁贷的跳了出来(深入浅出观察者模式)。
    我们的系统在整体上采用Spring framework来进行Bean管理。利用Spring通过配置文件来加载具体的类的方式——相当于被包装了的工厂,使得代码更加灵活。同样,观察者模式的应用可以很好的借用Spring framework提供的平台,变得更加灵活。
    首先创建了抽象观察者角色:
[php]/**
*  抽象观察者,用于处理相关的记录

*  @author Ai92
*  </pre>
*  Created on :2005-7-4 11:36:14
*  </pre>
*/
public abstract class StaffingObserver {   
   public abstract void update (Map para)throws BaseException;
}[/php]

由于仅仅是举例,这里只列出一个具体观察者角色:
[php]/**
*  Concrete Subject具体观察者角色。这里省略了业务代码

*  @author Ai92
*  </pre>
*  Created on :2005-5-30 14:59:11
*  </pre>
*/
public class TerminateDispatch extends StaffingObserver { 
  /**
    *  @param staffRecordID
    *  @throws BaseException
    *  @author Ai92
    *  <pre>
    *  Created on :2005-5-30 14:59:11
    *  </pre>
    */
   public void update(Map para) throws BaseException {
      //……do same business act
   }
   private DispatchDAO dispatchDAO ;
   /**
    * @param dispatchDAO 设置 dispatchDAO
    */
   public void setDispatchDAO(DispatchDAO dispatchDAO) {
      this.dispatchDAO = dispatchDAO;
   }
}[/php]


上面似乎没有什么特别之处。下面的代码就体现了在Spring framework下运用观察者模式的优势。
    在观察者模式中,目标角色是被客户程序调用,通过目标角色来通知具体观察者角色。在传统观察者模式中,目标角色中维护有一个观察者角色的列表,列表中的观察者角色通过其它程序进行添加维护——这就注定了观察者列表的改变会引起代码的修改。而通过Spring framework的配置方式注册观察者角色则避免了这种情况。
    先看下目标角色(在系统中我忽略了抽象目标角色),与Spring framework有关的代码用紫色标示:

[php]/**
*  目标角色,通知相应的观察者
*  @author Ai92
*  </pre>
*  Created on :2005-7-4 13:08:26
*  </pre>
*/
public class StaffingObservable {
    ……
    /**
     *  逐个获取观察者并通知他们来进行相应操作 
     *  @param para 观察者执行必要参数
     *  @throws BaseException
     *  @author Ai92
     *  </pre>
     *  Created on :2005-7-4 20:29:17
     *  </pre>
     */
    public void notifyForList(Map para) throws BaseException {
        Iterator it = allConfig.iterator();
        while (it.hasNext()) {
            String property = (String) it.next();
            StaffingObserver observer = (StaffingObserver) getBean(property);
            if (observer != null)
                observer.update(para);
            else {
                log.warn("bean '" + property + "'does not exist.");
            }
        }
    }
    private Object getBean(String beanName) {
        return TbApplicationContext.getWac().getBean(beanName);
    }
    /**
     * @param allConfig 设置 allConfig
     */
    public void setAllConfig(List allConfig) {
        this.allConfig = allConfig;
    }
    private List allConfig;
}[/php]

下面是Spring framework配置文件的一个片断——它应该出现在用到目标角色的具体客户程序对应的Spring配置文件中。它将具体观察者的别名存放在一个list里面,使用Spring为生成的目标角色注入。

[php]<!-- this the Spring config file for register Observers -->
<property name="obsevable">
    <bean class="com.ai92.service.TestObservable">                         <property name="allConfig">
             <list>
                <value>terminateDispatch</value>
<value>terminateParttime</value>
</list>
        </property>
</bean>
</property>[/php]

下面要做的就是要将具体观察者的别名与每一个具体观察者的类关联起来:

[php]<!-- the spring config file for Concrete Subject -->
<bean id="terminateDispatch" parent="baseTxProxy">
        <property name="target">
           <bean class="com.ai92.service.TerminateDispatch">
               <property name="dispatchDAO">
<ref bean="dispatchDAO"/>
</property>
           </bean>
        </property>
</bean>[/php]

这样,一个完整的观察者模式便在Spring framework中搭建起来了。在Spring framework的协助下,观察着模式变得更加灵活了。但是也增加了使用难度和调试难度,如果没有良好的命名机制、有帮助的注释和文档,初来乍到的人恐怕很难找到问题的所在。