在以Java開發Web-based應用程式時,多半會採用MVC(Model-View-Controller)的設計模式開發。軟體的分層架構中,領域層、使用者介面層,以及控制器層,就是分別對映到MVC設計模式中的三個角色。

提煉類別可抽離商業邏輯
使用Java開發Web-based應用程式時,現在相當流行使用Struts搭配Hibernate提供控制器層、領域層,以及資料存取層的機制。但是,我觀察到大多數使用Struts搭配Hibernate開發的團隊,它們的系統在處理來自使用者的請求時,Action除了處理來自瀏覽器的表單輸入值之外,還得一肩扛起很重要的工作,就是透過操作代表資料的Bean實作實際的商務邏輯。所以在許多專案中,所有的商業邏輯散落在各個Action類別之中。

各個Action會運用到的商業邏輯,其實時常重複,同時在Action類別中實作複雜的商業邏輯,也會使得Action類別本身過度冗長,這麼一來,就會讓Action類別演變成「相似或重複的程式碼」以及「過大的類別」。

為了解決這個問題,也許會有設計者嘗試利用「提煉類別」的技術,將這些在Action類別中會需要用到的商業邏輯,自Action類別中抽離,成為若干個獨立的類別。這樣的做法是個正確的方向,但我們可以更進一步界定這些被獨立出來的類別的角色,以及它們和其他角色之間的關係。

運用Façade塑造子系統的單一入口
設計模式中的Façade模式,十分適合做為對這個角色的定義。Façade這個字,是法文中建築物正面入口的意思。在軟體設計中,使用Façade這個模式指涉一組服務的唯一入口。

為什麼Façade很適合扮演實作Action類別所會需要的商業邏輯的角色呢?在系統中的各種不同的Action會需要用到各式的商業邏輯,對Action來說,這些商業邏輯便是對Action提供的服務,而且更重要的是,這些商業邏輯本身具備群組的關係,也就是說,多個商業邏輯之間會呈現出群組的現象。

例如,驗證使用者的帳號是否正確,以及檢查使用者的帳號是否已經過期,這兩個可能在Action類別中會運用到的商業邏輯,它們之間存在某種程度的相關性。這些位於同一群組的商業邏輯具備高聚合力(也就是彼此之間的相關度高),但與不同群組之間的耦合力卻相對地低(也就是和其他群組中的商業邏輯相關度低)。通常我們會將這樣的群組稱為系統中的子系統(Sub-System)。

以一個系統而言,它可以被畫分為多個子系統,例如對一個旅遊網站而言,整個系統可以被畫分為使用者帳號管理子系統、旅遊行程管理子系統、訂房管理子系統、訂單子系統、付款子系統……等。
在實作時,我們可以依據各商業邏輯在概念上所歸屬的子系統,將該商業邏輯封裝進入相對應的子系統中。例如,列出所有可供訂房的飯店、以及某一飯店尚有可訂房間的日期等等,都是可以歸屬在訂房管理子系統中。

降低相依性,控制修改的影響
之所以要組織系統中所有的商業邏輯,將它們畫歸到某個子系統,主要的原因是不希望客戶端程式碼(例如Action類別)任意地直接接觸各個商業邏輯。我們希望客戶端程式碼能夠從「正面入口」存取各項服務,而不是從四面八方而來。

約束客戶端程式碼從單一入口使用服務的方式,有利於日後的修改。因為這個做為單一入口的Façade,形同一個子系統的代表,封裝了子系統所提供的服務細節。

對於Façade的客戶端程式碼而言,Façade提供的是一個介面,而客戶端程式碼只需相依於這個介面,除非介面有所更動,否則客戶端程式碼都不會遭到波及。倘若這些商業邏輯並未加以封裝,那麼客戶端程式碼直接接觸到未經組織的各個商業邏輯,便很容易直接相依於實作的細節;日後實作細節若有所更動,也就會連帶影響客戶端程式碼。

我們可以如圖一,為整個系統增加一個Façade層,在系統中的每個Façade類別提供一組服務,而Action類別正是服務的使用者。在加入Façade層之後,Action類別不再直接實作商業邏輯,避免「相似或重複的程式碼」以及「過大的類別」這兩個毛病,而且抽離的商業邏輯,也以妥當的方式,安排到適當的子系統之中。

Façade類別封裝了相關度高的商業邏輯,從客戶端程式碼看來,Façade類別是服務的提供者,這使得客戶端程式碼可以用更高階的觀點來看待Façade中的商業邏輯,系統程式碼的可讀性也大為提升。也因為Façade對商業邏輯的封裝,使得商業邏輯不致於直接曝露在客戶端程式碼之前,即使日後有所改變,對客戶端程式碼的影響也能盡量降低。

介面的確立,有助於加速開發時程
Façade層次的確立,也能加速開發的時程。當我們決定使用Façade表示系統中的各個子系統,開始設計系統時,第一個動作便是找出系統中的子系統,接著決定各個子系統所提供的服務。

每個子系統都可以用一個Façade類別表示,而子系統所提供的各個服務,都是Façade類別介面中的一個Method。進入實作階段之後,Action類別以及Façade類別的實作者可以分頭進行,不需要等到Façade類別完成,才能開發Action類別。

雖然Action類別相依於Façade類別(因為Action類別使用到Façade類別),但是在Façade類別未完成時,Action類別和Façade類別的實作者,可以先依照Façade的介面提供一份虛有其表、未含真正實作的空殼Façade。空殼Façade中的Methods可以只回傳寫死的假值,而不需要回傳真正的運算結果。雖然只是假值,但這已能使得Action類別得以繼續開發。

在系統的分層架構中加入Façade層的好處相當多。當系統架構中的層次愈多時,意謂著系統有著更專職的角色分工以及責任畫歸,而且Façade通常也能帶來更多的好處。

層次的增加,固然可以增加系統的易讀性,但過多不必要的層次,有時會因為層層相疊、過於間接,結果反而降低了系統的易讀性,這是在系統分層架構中加入新的層次時,所必須慎重考量的。

《作者簡介》王建興
清華大學資訊工程系的博士研究生,研究興趣包括電腦網路、點對點網路、分散式網路管理、以及行動式代理人,專長則是Internet應用系統的開發。曾參與過的開發專案性質十分廣泛而且不同,從ERP、PC Game到P2P網路電話都在他的涉獵範圍之內。

相關閱讀:
物件導向程式設計常見的錯誤(1)抽離作用重複的程式碼,重構品質
物件導向程式設計常見的錯誤(2)責任分配均衡才是健康的系統
物件導向程式設計常見的錯誤(3)立體的系統架構,可降低修改的影響
物件導向程式設計常見的錯誤(5)工程化的考量不可走火入魔

熱門新聞

Advertisement