做軟件架構設計的時候,三層式體系結構是一個很常套用的分層模式。三層式體系結構中會將系統切割成為:PL(Presentation Layer)、BLL(Business Logic Layer)、DAL(Data Access Layer)。在這其中BLL是整個系統的系統核心,而DAL則是數據對象進出系統的系統邊界。本篇文章討論實作系統時,系統核心與系統邊界之間的架構如何設計,才能提高內聚、減少耦合。
我們先假設系統設計時,將BLL、DAL各自獨立為一個DLL。而BLL里有Control對象操作Object對象、ObjectReposository對象用來提供功能給PL使用、DAL里則有ObjectReposository對象來封裝Object對象進出系統邊界的職責。這樣的系統架構下,會發現Object對象,同時被BLL及DAL所使用。這個共享的Object對象,在系統里該如何存在是一件很有趣的問題。
思考Object對象在系統里該如何存在這件事,以最直覺方式來思考的話,會認為BLL層是「使用」Object對象,而DAL只是讓Object對象「通過」。那Object對象歸類進BLL里,應該是可以接受的設計。
實作上卻會發現,BLL里的Control對象要操作ObjectReposository對象,所以BLL層相依DAL層。而將Object對象歸類進BLL里,ObjectReposository對象要操作Object對象,又必須DAL相依BLL。這兩個相依,造成了設計上的循環相依。
Object對象放進BLL會有循環相依的問題,那將Object對象放進DAL也是會有同樣的問題發生。那干脆就將Object對象封裝成一個獨立DLL,這樣的設計避免BLL、DAL之間的循環相依,也提供了彈性讓DAL、Object對象的組合可以重用。
實作上這個設計可以正常工作,但這是架構在「Object對象為貧血對象」這個前提上。假設Object對象不是貧血對象實作了部分系統功能,而BLL實作其他系統功能,當Object對象要重用BLL的系統功能時,就會造成循環相依的問題。而Object對象如果實作了所有系統功能,那BLL的存在就顯得多余。
在網絡上或設計架構書籍里可以看到,將BLL要使用的邊界對象設計為接口封裝成一個獨立DLL,並且BLL相依於這個接口、DAL實作這個界面。這樣的設計避免BLL、DAL之間的循環相依,也提供了彈性讓BLL可以抽換DAL實作。
實作上這個設計可以正常工作,但還是架構在「Object對象為貧血對象」這個前提上,無法解決這個前提造成的種種問題。
問題的解決方案其實沒那么困難,回到最直覺的設計,將Object對象歸類進BLL里,並且在BLL與DAL之間套用IoC,反轉Control對象與ObjectReposository對象之間的相依性。這樣的設計避免BLL、DAL之間的循環相依、提供了彈性讓BLL可以抽換DAL實作,並且加強了BLL的內聚、減少BLL的耦合、……。。
但是這樣的設計在遇到重用Object對象的情景,例如:使用WCF來傳遞Object對象時,則必須要去考慮不同上下文(Context)關系來做設計。而「上下文關系」是另外一個有趣的議題,並且包含很多技術內容。限於篇幅的關系,這部分內容另外開主題再來討論。
本篇文章討論實作系統時,系統核心與系統邊界之間的架構如何設計,並介紹提高內聚、減少耦合的解決方案。這些基礎面向對象設計概念,閱讀起來會比較無趣,並且實作時也會遇到很多的困難。但仍希望開發人員能夠花點時間投資,畢竟現在很多新技術,都是靠這些面向對象的設計去堆砌出來的。