簡介: 在定義了架構願景之後,團隊中的所有人員應該對待開發的軟體有一定的瞭解了。但是,面對一個龐大的軟體
系統,接下來要做些什麼呢?分而治之的思想是電腦領域非常重要的思想,因此我們也從這裡開始入手。要進行應用軟體的設計,分層是非常重要的思想,掌握好
分層的思想,設計出的軟體是可以令人賞心悅目的。由於這一章的重要性和特殊性,本章的內容分為上下兩節,並不採模數式描述語言的方式。
分層只是將系統進行有效組織的方式。
本章特別針對於公司專屬應用程式進行討論,但其中大部分的內容都可以應用在其它的系統中,或為其它的系統所參考。
在公司專屬應用程式中,有兩個非常重要的概念:商務邏輯和持久性。可以說,公司專屬應用程式是圍繞著商務邏輯進行開展的。例如報銷、下訂單、貨品入庫等都是業務邏
輯。從商務邏輯的底層實現來看,商務邏輯其實是對業務實體進行組織的過程。這一點對於物件導向的系統才成立,因為在物件導向的系統中,識別業務實體,並制
定業務實體的行為是非常基礎的工作,而不同的業務實體的組合就形成了商務邏輯。
還有另一個重要的概念是持久性。公司專屬應用程式中大部分的資料都是需要可持久化的。因此,基礎組織支援持久性就顯得非常的重要。目前最為通行的支援持久性的機制是資料庫,尤其是關係性資料庫-RDBMS。
除此之外,在公司專屬應用程式中還有其它的重要概念,例如人機互動。
為了能夠更有效對企業中的各種邏輯進行組織,我們使用層技術來實現公司專屬應用程式。層技術在電腦領域中有著悠久的曆史,電腦的實現中就引用了分層的概念。TCP/IP的七層協議棧也是典型的分層的概念。分層的優勢在於:
上層的邏輯不需要瞭解所有的底層邏輯,它只需要瞭解和它鄰接的那一層的細節。我們知道TCP/IP協議棧就是通過不同的層對資料進行層層封包的,不同層間的耦合度明顯降低。通過嚴格的區分層次,大大降低了層間的耦合度。
某一層次的下級層可以有不同的實現。例如同樣的程式設計語言可以在不同的作業系統和不同的機器中運行。
和第三條類似的,同一個層次可以支援不同的上級層。TCP協議可以支援FTP、HTTP等應用程式層協議。
綜合上面的考慮,我們把公司專屬應用程式分為多個層次。公司專屬應用程式到底應該分為幾種層次,目前還沒有統一的意見。
在前些年的軟體開發中,兩層結構佔有很重要的位置。例如在銀行中應用很廣的大型主機/終端方式,以及Client/Server方式。兩層的體繫結
構一直到現在還廣泛存在,但是兩層結構卻有著很多的缺點,例如用戶端的維護成本高、難以實現分散式處理。隨著在兩層結構的終端使用者和後端服務間加入更多的
層次,多層的結構出現了。
經典的三層理論將應用劃分為三個層次:
展示層(Presentation
Layer),用於處理人機互動。目前最主流的兩種展示層是Windows格式和WebBrowser格式。它主要的責任是處理使用者請求,例如滑鼠點擊、輸入、HTTP請求等。
領域邏輯層(Domain Logic Layer),類比了企業中的實際活動,也可以認為是企業活動的模型。
資料層(Data source Layer),處理資料庫、訊息系統、事務系統。
在實際的應用中,三層結構有一些變化。例如,在Windows的.NET系統中,把應用分為三個層次:展示層(Presentation
Layer)、業務層(Business Layer)、資料訪問層(Data Access
Layer),分別對應於經典的三層理論中的三個層次。值得一提的是,.NET系統中展示層可以直接存取資料訪問層,即記錄集技術。在ADO.NET中,
這項技術已經非常成熟,並通過展示層中的某些資料感知組件,實現非常友好的功能。這種越層訪問的技術通常被認為是不被允許的,因為它可能會破壞層之間的依
賴關係。而在Windows平台中,嚴格遵守準則就意味著需要大量額外的工作量。因此,我們看到準則也不是一成不變的。
在J2EE的環境中,三層結構演變為五層的結構。在展示層這裡,J2EE將其分為運行在客戶機上的使用者層(Client
Layer),以及運行在服務端上的Web層(Presentation Layer)。這樣做的主要理由是Web
Server已經成為J2EE中非常核心的技術,例如JSP和Java Servlet都和它有關係。Web層為使用者層提供表示邏輯,並對使用者的請求產生回應。
業務層(Business Layer)並沒有發生變化,仍然是處理應用核心邏輯之處。而資料層則被劃分為兩個層次:整合層(Integration
Layer)和資源層(Resource
Layer)。其中,資源層並非J2EE所關心的內容,它可能是資料庫或是其它的老系統,整合層是重要的層次,包括交易處理,資料庫映射系統。
執行個體
這一章的的組織方式和之前的模式有一些差別。我們先從一個例子來體會架構設計中分層的重要性。
是一個業務處理系統的軟體架構圖。在中,我們把軟體分為四個層次。這種階層類似於我們前面所談的J2EE的分層。但是和J2EE不同的是,缺少了一個Web
Server層,這是因為目前我們還沒有任何對Web Server的需要。
在資源層上,我們有三種資源:資料庫、平台服務、UI。資料庫是公司專屬應用程式的基礎,而這裡的平台服務指的是作業系統系統或第三方軟體提供的交易管理員
的功能。值得注意的是,這裡的交易管理員指的並不是資料庫內部支援的事務,而是指不同的業務實體間交易處理,這對於公司專屬應用程式有著很重要的意義。因為對於一
個公司專屬應用程式來說,常常需要處理跨模組、跨軟體、甚至跨平台的會話(Session),這時候,單純的資料庫支援的事務往往就難以勝任了。這方面的例子包括
微軟的MTS和Oracle的DBLink。當然,如果說,在你處理的系統中,可以使用資料庫事務來處理大部分的會話的話,那就可以避免考慮這方面的設計
了。除了典型的交易管理員,平台還能夠提供其它的服務。對於大部分的公司專屬應用程式來說,都是整合了多個的平台服務,平台服務對架構的設計至關重要。但是從分層
的角度上考慮,上層的設計應該儘可能的和平台無關。和使用平台服務類似的,一般來說,公司專屬應用程式都不會從頭設計介面,大部分情況下都會使用現有的的UI資
源。比如Window平台的MFC中的介面部分。因此,我們把被使用的UI資源也歸到資源層這個層次上。
資源層的上一層是整合層。整合層主要完成兩項工作,第一項是使用資源層的平台服務,完成公司專屬應用程式中的交易管理。有些交易處理機制已經提供了比較好封
裝機制,直接使用資源層的平台服務就可以了。但是對於大多數的應用來說,平台提供的服務往往是比較簡單的,這時候整合層就派上大用場了。第二項是對上一層
的對象提供持久性機制。可以說,這是一組起到過渡作用的對象。它實際上使用的是資源層的資料庫功能,並為上一層的對象提供服務。這樣,上一層的業務對象就
不需要直接同資料庫打交道。對於那些底層使用關係型資料庫,編程中使用物件導向技術的系統來說,目前比較常見的處理持久性的做法是對象/關係映射(OR
Mapping)。
在這個層次上,我們可以做一些擴充,來分析層的作用。假設我們的系統需要處理多個資料庫,而不同資料庫之間的處理方式有一定的差異。這時候,層的作
用就顯示出來了。我們在整合層中支援對多個資料庫的處理,但對整合層以上的層次提供統一的介面。對於業務層來說,它不知道,也不需要知道資料庫的差別。目
前我們自己開發了整合層中的持久類,但是隨著功能的擴充,原有的類無法再支援新增加的功能了,新的解決方案是購買商用程式。為了儘可能的保持對業務層次的
影響,我們仍然使用原有的介面,但是具體的實現已經不同了,新的代碼是針對新的商業程式來實現的。而對業務層來說,最理想的狀況是不需要任何的改變。當然
現實中不太可能出現如此美好的情況,但可以肯定的一點是,引入層次比不引入層次要好的多。
以上列舉的兩個例子都是很好的解決了耦合度的問題。關於分層中的耦合度的問題,我們在下面還會討論。
業務層的設計比較簡單,暫時只是把它實現為一組的業務類。類似的,展示層的設計也沒有做更多的處理。展示層的類是繼承自資源層的。這是一種處理的方
法,當然,也可以是使用關係,這和具體的實現環境和設計人員的偏好都有關係,並不是唯一的做法。在對軟體的大致架構有了一個初步瞭解之後,我們需要進一步
挖掘需求,來細化我們的設計。在前面的設計中,我們對業務層的設計過於粗糙了。在我們的應用中,還存在一箇舊系統,這個系統中實現了應用規則,從應用的角
度來看,這些規則目前仍然在使用,但新的系統中會加入新的規則。在新系統啟用後,舊的系統中的規則處理仍然需要發揮它的作用,因此不能夠簡單的把所有的規
則轉移到新系統中。(有時候我們是為了節省成本而不在新系統中實現舊系統的邏輯)。我們第二步的架構設計的細化過程中將會加入對新的要求的支援。
在細化業務層的過程中,我們仍然使用層技術。這時候,我們把原先的業務層劃分為兩個子層。對於大多數的公司專屬應用程式來說,業務層往往是最複雜的。企業對
象之間存在著錯綜複雜的聯絡,企業的流程則需要用到這些看似獨立的企業對象。我們希望在業務層中引入新的機制,來達到組織業務類的目的。業務層的組織需要
依賴於具體的應用環境,金融行業的應用和製造行業的應用就有著巨大的差距。這裡,我們從一般性的設計思考的角度出發來看待我們的設計:
首先,我們看到,業務層被重新組織為兩個層次。增加層次的主要考慮是設計的重用性。從我們前面對層的認識,我們知道。較高的層次可以重用較低的層
次。因此,我們把業務層中的一部分類歸入較低的層次,以供較高的層次使用。降低類層次的主要思路是分解行為、識別共同行為、抽取共性。
在Martin Fowler的分析模式中提供了一種將操作層和知識層分離的處理方法:
Action、Accountability、Party屬於較高層次的操作層(Operational
Layer),這一層次的特點是針對於特定的應用。但是觀察Accountability和Party,有很多相似的職責。也就是說,我們對於知識的處理並不合適。因此,Martin
Fowler提出了新的層次――屬於較低層次的知識層(Knowledge
Layer)。操作層中可重用的知識被整理到知識層。從而實現對操作層的共性抽取。注意到雖然圖中的層次(Level)的概念和層(Layer)有所差別,但是思路是基本一致的。
另一種分層方法是利用繼承:
該圖中也是來自於分析模式一書。不同的部門有著差異點和共性,將共性提取到父類中是繼承的基本概念。這時候我們可以把父類看作是較低的層次,而把子類看作是較高的層次。對於一組階層很深的類來說,也可以從某一個水平線上分離層次。
最後一種方法需要分解並抽象對象的行為。C++的STL中為不同的資料類型和不同的容器實現了Iterator的功能。它的實現是典型的降低層次的
行為。我們不考慮它對不同資料類型的支援,因為它使用了比較獨特的模板(Template)技術,只考慮它對不同的容器的實現。首先是對共性的分析,不論
是數組(Array)還是向量(Vector),要執行遍曆操作,都需要知道三個條件:容器的起始點、容器的長度、匹配值。這就是一種抽象。通過這種方
式,就可以統一不同容器的介面。
以上我們討論了細分層次的好處和實現策略。下面我們回到前例中,繼續討論層中的各個組件。
業務實體指的是企業中的一些單獨的對象。例如訂單、客戶、供應商等。由於業務實體可以被很多的商務程序(業務會話)所使用,因此我們把業務實體放在
業務實體層中。企業中的業務實體大多是需要持久性的。因此在我們的設計中,業務實體將持久性的職責委託給下一個層次中的持久性包,而不是直接調用資料庫方
法。另一種常用的方法是使用繼承――業務實體繼承自持久類。EJB中的Entity
Bean就是典型的業務實體,它可以支援自動的持久性機制。
在我們的系統中,規則是一個尷尬的存在。部分的規則處於舊系統中,並使用舊系統的持久性機制。而新系統中有需要支援新的規則。對於上層的使用者來說,
並不需要知道新舊系統的規則。如果我們在使用規則時做一個新舊規則的判斷並調用不同的系統函數,那就顯得太傻了。我們的設計對上層而言應該是透明的。
所以我們總共設計了三個包來實現規則的處理。封裝器封裝了舊系統中的規則,這樣做處於幾點考慮:首先,舊系統是面向過程的,因此我們需要用封裝器將
函數(或過程)封裝到類中。其次,對舊系統的使用需要額外的程式(或平台服務)的支援,因此需要單獨的類來處理。最後,我們可以使用Adapter模式
[GOF
94]將新舊系統不同的介面給統一起來,以使他們能夠一起工作。新的規則類實現了新的規則,較好的做法是定義一個虛協議,具體的表現形式可以是虛基類或接
口。然後由其子類實現虛協議。再結合前面介紹的把舊規則的介面轉換為新的介面的方法,就能夠統一規則介面。我們看到,業務實體需要使用規則,通過統
一的介面,業務實體就能夠透明的使用新舊兩種規則。
在定義了規則和標準的規則介面之後,上一層的規則控制器就可以通過調用規則,來實現不同規則群組合。因此這個規則控制器就類似於應用規則。在業務交易處理中需要調用規則控制器來實現部分的功能。
請注意,這裡討論的思路雖然非常的簡單、清晰,但是現實中的處理卻沒有這麼容易。因為為新舊規則制定統一的介面實在是太難了。要能夠使介面在未來也
相對的穩定更是難上加難。而在應用已經部署之後,對發行的介面的任何一個小改動都意味著很高的成本。在這個問題上,並沒有什麼好的策略,經驗是最重要
的。在實際中的一個比較實用的方法是為介面增加一些額外的參數。即便可能這個參數當前並沒有使用到,但是如果為了有可能使用的話,那還是加上吧。舉個例
子,對於商業應用軟體而言,資料庫,或者說是關聯式資料庫一定是不可缺少的部分。大量的操作都需要和資料庫結合起來,因此,可以考慮在方法中加入一個資料庫
連接字串的參數。雖然這對於很多方法而言是沒什麼必要的,但是為將來的實踐提供了一個可擴充的空間。國內的某個知名的ERP軟體的設計就採用了這種思
路。
本章的主要精力都放在執行個體的研究上,在有了一個基本的概念之後,我們再回來談談分層的一些原則和需要注意的問題。