如何設計良好的系統架構(clean code閱讀筆記之十)

設計良好的系統

注:正文中的引用是直接引用作者的話,兩條橫線中間的段落的是我自己的觀點,其他大約都可以算是筆記了。

本章講的是「如何設計良好的系統架構」,讀起來比較困難,不論是從結構上還是從文字上。結構上作者從「建設一個城市」開講,之后花了很大的篇幅講面向切面編程,最后又加上了幾個方法論上的東西。文字上本章作者很多說法使用的單詞和其他地方看到的的略有不同。但回過頭來看,作者的思想是一脈相承的,整個章節其實只講了一件事情——隔離。

本文會一直提到一個詞「關注」,原文中使用的是concern,表達的是在軟件開發中對某個問題的解決方案或者某個模塊擔任的職責(感覺還是不夠貼切)。


本章有一個「結論」的小節,就先把她放上來了,內容在后邊的小節里慢慢展開。

結論

  1. 系統整體架構的設計同樣需要簡潔優雅(作者此處指的是系統各個抽象層面之間要解耦),不然會帶來各種各樣的壞處;
  2. 在所有的抽象層面上,系統的目的要清晰(此處指的是單一職責,非侵入性)。為了達到這個目的,請使用AOP;
  3. 不論你是在設計一個系統,或者一個單獨的模塊,永遠不要忘記使用最簡單的實現

你會如何去建設一個城市

構建一個系統就像構建一個城市,一個龐大的城市系統之所以能夠運轉,可以歸結為兩個原因:

  1. 隔離不同的關注點。不同的人有不同的分工,有些人關注細節,有些人關注大局
  2. 包含多個抽象層面。城市系統有許多個不同的抽象層面和模塊,使得所有管理城市的單個的人不需要關心整個城市運轉的方法也可以一起高效的工作來維持整個城市。

區分「建設」一個系統和「使用」一個系統

對于一個建筑,建設它的時候可能是各種起重機和鋼筋混凝土,在里邊工作的工人們戴著安全帽穿著工作服,可是當它建成之后在里邊工作的工人們可能會是穿著整潔的西裝,完全是另外一番景象。

對于一個軟件系統也是如此,「啟動過程」要關注的事情和之后的「運行過程」是不同的——「關注點分離」是OOP的一個基本理念——這也是本章首先要探討的第一個「關注」。

很多的系統里都會有代碼10-1這樣的懶加載的代碼:

//代碼10-1
public Service getService() {
    if (service == null)
        service = new MyServiceImpl(...); // Good enough default for most cases?
    return service;
}

這種代碼可以帶來很多好處……但是,不好的地方在于此處接口Service依賴于具體的實現類MyServiceImpl和所有它的構造器所依賴的資源,同時但它進行單元測試也會出現問題,擁有此方法的類需要關心MyServiceImpl的內部情況。另外,偶爾出現一兩處這種代碼還好,如果整個系統里存在各種各樣的這樣的初始化代碼,那么通常會出現很多的重復性代碼。一個好的解決方法就是把初始化的過程從業務邏輯中抽離出來。

使用main進行分離

一種分離構建過程的方法是簡單地把所有初始化的過程放在main里或者main所調用的模塊里。那么系統的依賴關系如下圖所示。

把構建過程放在main()里

這樣程序的業務邏輯就不需要知道main()或者它構造的對象的邏輯,所有它需要的對象都會被main()來裝配好。

使用工廠方法進行分離

使用main的方法會導致一個問題,所有程序所需要的對象都會在啟動的過程中被創建,這會造成很大程度的資源浪費,那么另一個可以讓程序自己來決定什么時候創建對象,但同時又分離了創建過程細節的方法則是使用抽象工廠方法,依賴關系如下圖所示

使用抽象工廠來分離

從圖中可以看出,當程序(OrderProcessing)需要創建LineItem時,它去調用工廠方法來具體地執行生成對象的過程,程序只需要關系何時調用和生成什么樣子的對象。

依賴注入

要實現創建過程和使用過程的分離,一個更強大的方案就是實用依賴注入。依賴注入將生成并裝配對象的職責包裹到了一些專用的對象中去,這樣就實現了「單一職責」原則。依賴注入的本意是指一些類不直接處理自己的依賴,相反是在類中定義諸如「特定參數的構造器函數」或「getter setter方法」,通過這些方法將依賴注入進來。在構建過程中,依賴注入容器初始化所需的對象,然后使用這些方法注入到這個類的實例中去。

擴展(Scale Up)

如同城市是從小的定居點發展而來一樣,任何系統都一定是從小系統不斷發展成為大系統的,想要「從一開始就把系統設計的足夠完善」是不可能的,相反的,我們應該「只關注當下的需求」,然后再不斷地對系統進行重構,對系統進行遞進式擴展。

在代碼層面上,TDD,重構和clean code可以幫助進行這樣的工作;在系統層面,如果我們適當地對關注點進行隔離,軟件系統是可以實現遞進式擴展的。

接著作者拿EJB來舉了個反例,在EJB1或EJB2中,很多業務邏輯和容器有緊耦合的關系,要實現一個Bean,你必須實現很多容器所提供的生命周期方法,這種緊耦合就導致了分離的單元測試很難進行,同時一個bean也不能通過繼承另一個bean來實現功能的擴展,這就導致了OOP概念被破壞,會導致系統中存在冗余數據。

切面

很多關注點(像持久化這樣的)通常是跨越不同層次的對象的,原理上說,這些關注應該被封裝在一個單獨的模塊內,但實際中這種邏輯往往是跨越不同層次的對象的。這就引入了AOP的概念,AOP往往是解決像持久化、安全、事務等這種在某個切面上的關注點的通常方法。使用一些AOP的框架可以在實現最大限度的非侵入性的前提下解決切面的關注點的需求,下面將列舉Java中3種AOP的解決方案。

Java代理

在簡單的AOP的需求情景里,使用JDK自帶的代理來做是比較合適的。使用Java代理,必須使用Interface,不然就要使用CGLIB,ASM或者Javassist等字節碼修改庫。

Java代理的優點在于簡單,缺點則是對于簡單的邏輯來說代碼稍微有點復雜,你必須去定義一個傳出對象的接口,然后實現一個動態代理的接口等等,還有就是實用Java代理不能實現一個全系統的切面。

純Java AOP框架

作者在這一節里講了Spring的AOP方法,它實現了業務邏輯和切面的解耦。

AspectJ

Spring和Jboss等純Java的AOP框架可以覆蓋百分之80到百分之90的情況,但AspectJ提供了更加豐富和強大的面向切面編程工具,AspectJ的缺點就是你需要使用一些新的工具和學習新的使用方法和語言結構。

使用測試來驅動系統架構

使用像AOP這樣「隔離關注點」的編程方法可以帶來數不盡的好處,它使得我們在代碼層面避免了很多緊耦合實現,因為很多邏輯都可以抽象到切面或其他不同的關注點里,留下的邏輯可以在簡單的POJO中實現,這就使得真正的使用測試來驅動你的系統架構成為可能。

另外,任何系統都是從不完美慢慢發展而來的,永遠不要想著在開始就設計好了全部的事情(BDUF, big design up front),BDUF往往會帶來很多壞處。對于建筑行業來說,他們必須使用BDUF,因為他們的架構一旦建好就不能更改了,然而軟件系統有著自己獨特的物理結構,使得去做一些較徹底的改動成為可能。

所以一個軟件系統的建設應該是這么一個過程,首先是設計一個「簡單的但是良好解耦」的架構,快速的完成客戶的需求,然后隨著需求的變化不斷的進行擴展。許多優秀的網站,有著復雜的數據緩存機制,安全機制,虛擬化機制等等,這些都是因為在每一個抽象層面上它都很「簡單」。

優化決策方法

模塊化和關注點分離使得「去中心化的管理和決策」成為可能。不論是一個軟件系統或者一個城市的建設,沒有一個人可以對所有的事情都做出有建設性的決策。

很多時候我們習慣于在系統設計的一開始,就想好所有的可能情況,但是最好的做法應該是「把決定放在最后一刻去做」,這并不是懶或者不負責任,這可以使得我們所有的決定都是基于最大可能知道的知識而做出的

更聰明地使用標準

就像建筑行業一樣,在長久的實踐中,在軟件系統開發的行業里也積累了很多的標準,如EJB,這些標準使得想法和系統組件得以復用。但是有時候一個標準的使用已經到了會給開發帶來各種問題的地步,如果你有更好的方法(但是沒有遵守現有的標準)來實現客戶價值的時候,不需要死板的遵守標準。

系統設計需要DSL(領域專用語言)

合理的使用DSL,可以幫助對系統中的不同概念進行抽象,系統的設計者使用DSL來設計系統,可以擺脫具體實現細節的束縛,能夠好地對系統各個模塊進行抽象設計。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,065評論 25 708
  • 團隊開發框架實戰—面向切面的編程 AOP 引言 軟件開發的目標是要對世界的部分元素或者信息流建立模型,實現軟件系統...
    Bobby0322閱讀 4,174評論 4 49
  • 今天大風凜冽 我還是愿去看你 我在風中賽跑 只為贏得更多時間 與你在一起 看你每天只能躺著 或直直的坐著 你身體難...
    路上過客閱讀 386評論 18 12
  • 有些人之間是會有心靈感應的,你難過的時候我會情緒低落,你開心的時候我會莫名輕松,你焦慮的時候我會煩躁……因為在意因...
    兮兮0225閱讀 250評論 0 0
  • 英國倫敦的戴爾·艾哈邁德博士以圣巴塞洛繆醫院為基地,創立了 Medical Realities 醫療培訓機構,目的...
    ty晌午之月閱讀 377評論 0 1