模式實踐:觀察者模式與Spring
公司的專案告一段落,最近在進行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通過配置檔案來載入具體的類的方式——相當於被包裝了的工廠,使得程式碼更加靈活。同樣,觀察者模式的應用可以很好的借用Springframework提供的平臺,變得更加靈活。
首先建立了抽象觀察者角色:
/** * 抽象觀察者,用於處理相關的記錄 * @author Ai92 * </pre> * Created on :2005-7-4 11:36:14 * </pre> */ public abstract class StaffingObserver { public abstract void update (Map para)throws BaseException; } |
由於僅僅是舉例,這裡只列出一個具體觀察者角色:
/** * 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; } } |
上面似乎沒有什麼特別之處。下面的程式碼就體現了在Spring framework下運用觀察者模式的優勢。
在觀察者模式中,目標角色是被客戶程式呼叫,通過目標角色來通知具體觀察者角色。在傳統觀察者模式中,目標角色中維護有一個觀察者角色的列表,列表中的觀察者角色通過其它程式進行新增維護——這就註定了觀察者列表的改變會引起程式碼的修改。而通過Spring framework的配置方式註冊觀察者角色則避免了這種情況。
先看下目標角色(在系統中我忽略了抽象目標角色),與Springframework有關的程式碼用紫色標示:
/** * 目標角色,通知相應的觀察者 * @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; } |
下面是Spring framework配置檔案的一個片斷——它應該出現在用到目標角色的具體客戶程式對應的Spring配置檔案中。它將具體觀察者的別名存放在一個list裡面,使用Spring為生成的目標角色注入。
<!-- 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> |
下面要做的就是要將具體觀察者的別名與每一個具體觀察者的類關聯起來:
<!-- 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> |
這樣,一個完整的觀察者模式便在Spring framework中搭建起來了。在Spring framework的協助下,觀察著模式變得更加靈活了。但是也增加了使用難度和除錯難度,如果沒有良好的命名機制、有幫助的註釋和文件,初來乍到的人恐怕很難找到問題的所在。