公司的項目告一段落,最近在進行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的協助下,觀察著模式變得更加靈活了。但是也增加了使用難度和調試難度,如果沒有良好的命名機制、有幫助的注釋和文檔,初來乍到的人恐怕很難找到問題的所在。