軟件架構的靈活設計
板橋里人html
軟件架構如同人的骨架,不但要在總體上有骨感,並且細部須要不少骨關節鏈接,骨關節能夠把兩根大骨銜接在一塊兒,兩根大骨由此造成了鬆耦合,這樣整個骨架的活動就靈活自如了。
軟件架構也應該如此,組件之間實現鬆耦合,相似積木或樂高玩具同樣,經過組件模塊之間的鬆耦合構建成一個靈活自如的軟件系統。算法
鬆耦合表明對象之間關係比較鬆散,甚至沒有熱河關係,鬆耦合能夠帶來軟件架構的靈活性,意味着擴展性、可維護性獲得提升,複雜性下降了。網絡
目前,組件模塊之間鬆耦合常見有兩種方式:接口和消息,這二者如同骨關節同樣,鏈接着鬆耦合的雙方。消息比接口更加鬆耦合,由於接口方法有可能改變,致使接口的使用者跟着變化,而經過消息,消息生產者只要發送消息到消息系統就能夠了,再也不管是誰來接受消費這個消息,消息生產者由此和消費者徹底解耦了。架構
複雜性
假設原來是隻有兩個組件進行交互:
ide
後來需求不斷擴展變化,增長了第三個組件,那麼這三個組件之間任意交互,複雜度就會提升:
複雜度以指數級的增加是驚人的,當咱們增長到六個組件,複雜度將是驚人的:
函數
天然界是如何應對這複雜呢?這在物理中被稱爲構造定律 Constructal Law, 僅有的咱們知曉的大天然是如何指導複雜演化的規律(能夠說是上帝創造萬物的方式),是由Adrian Bejan於1995創立的構造定律:學習
For a finite-size system to persist in time (to live), it must evolve in such a way that it provides easier access to the imposed currents that flow through it.flex
對於一個有限大小的持續活動的系統,它必須以這種方式發展演進:它提供了一種在自身元素之間能量(current)更容易流動的方式。
構造定律被提出做爲對全部的設計和進化進行論述的物理學第一個原則。它認爲形狀和結構的出現是爲了更好地流動。大天然的這種設計反映了這樣趨勢:它鼓勵實體能更容易流動 – 在一樣的每單位能量消耗的狀況下能更容易移動。例如雨滴彙集在一塊兒,匯成河流,江流入海成大海,大天然的設計使得水更容易流動。
構造定律致力於描述能量和物質在物理網絡(如河流)和生物網絡(如血管)中的流動,這個理論提出,若是一個流體系統(flow system)要繼續存在(好比,生存),那麼他必須始終提供更容易的方式來得到這個系統中的流體。換句話說,系統應該致力於將能量消耗減小到最低限度,而同時將消耗單位能量產生的熵提升到最大限度
Bejan相信,進化實質上是這麼一個過程,即生物體不斷的重組他們自身,以使能量和物質可以儘量迅速高效的經過他們。更好的流體結構(flow structure),無論它們是動物仍是河流,將取代那些較差的結構。。
對於咱們軟件系統,如何設計出一種結構以促成流經這個結構的用戶請求能更有效地得到響應呢?很顯然,前面那種多個組件發生任意關聯的方式確定是不經濟的,熵值反作用很大,若是變成如下這種結構,組件可以實現分組,三個元素是一組,另一組是四個元素,組與組之間經過一個表明元素關聯:
那麼,如何進行這種有合有分的設計呢?
SOLID原則是關鍵:
S - Single Responsibility Principle 單一職責,簡稱SRP
O - Open/Closed Principle 開閉原則
L - Liskov Substitution Principle 里氏替換原則 簡稱LSP
I - Interface Segregation Principle接口分離 簡稱ISP原則
D - Dependency Inversion Principle 依賴反轉原則 DIP
這五大設計原則中,單一職責功能是關鍵,它意味着它只作一件事。
它與讓事情DRY原則是一致的:每個知識都必須有一個單一的、明確的、在一個系統內的權威明確表示。不要重複,須要乾脆。 程序中的每個重要的功能都應該在源代碼中的一個地方實現,將業務規則、長表達式,if語句、數學公式和元數據等各自放在一個地方。
單一職責也與委託原則有關,只有放棄一些才能得到一些,有舍有得,放棄的就是委託其餘類實現:不要本身作全部的事情,能夠委託給相應的類去完成。
由於每一個組件都秉持單一職責,組件之間纔可能發生惟一的一個關聯關係:
url
鬆耦合與高凝聚
軟件設計者應該是分離應該分離事物,而不是將本應一塊兒考慮的事情進行分離。
-Kent Beckspa
實現鬆耦合,不是簡單地切分就能夠,須要如庖丁解牛遵循必定天然之道,若是切分了不應切分的事物反而事倍功半。因此,反者道之動,咱們首先了解什麼是不應分離的事情,才明白剩餘的纔是應該分離的。
不可分離的關係也稱爲凝聚,而凝聚好像是一種耦合,是鬆耦合的反面,那麼如何區分耦合與凝聚的區別呢?凝聚與耦合都是表達模塊之間關係的名詞,可是存在不一樣含義。
凝聚有高凝聚和低凝聚之分,高凝聚是指那些自然聚合在一塊兒的緊密關係,好比總體是由部分組成的關係,業務領域中業務焦點等等,高凝聚要麼是表明業務強關係,要麼是體現其餘優良性狀的關聯,如可靠性、健壯性、可重用性和可理解性。
而低凝聚則是與不良特徵有關,包括難以維持、難以重用和難以理解。在業務領域表現爲那些無關緊要的關聯關係,沒法肯定的關聯關係,多對多的關聯等,當你發現兩個實體之間是多對多關係時,可能意味着你是執着於關係角度沒事找事地找出它們之間的關係,從另一個角度來看,萬物互聯,中醫還認爲天人合一,這些都不是科學的分析方法,科學分析方法是將關係切分爲高凝聚與低凝聚兩種,而後排除低凝聚,只留下高凝聚,其他都是鬆耦合,這樣纔有的放矢。
咱們還能夠從類的方法功能上判斷高凝聚,固然前提是你的實體必須有方法行爲,而不是隻有屬性的失血模型。若是嵌入在類中的功能有不少共同之處,那麼系統中的內聚性就會增長。也就是說,若是一個類的方法行爲確實有不少共同之處,那麼表明這些方法與類確實有一種內聚性,若是隨着方法行爲增多,這種類的內部高聚性就愈來愈強。
高內聚可以提升了模塊的可重用性,模塊中不少功能都有共同點,所以開發人員很容易在模塊中找到他們想尋找的功能。
從另一個方面來看,若是業務領域的邏輯改變,卻不多影響周圍其餘模塊,這說明系統的可維護性提升了,由於你的業務邏輯都已經收斂到應該收斂的地方,不會散落各處,因此就不多影響其餘模塊。如同玉皇大帝爲何要找回七仙女,由於他們都是仙人,仙人就該回(收斂或凝聚)到他們本身的地方。
同時,當代碼有高凝聚時,意味着模塊的複雜性下降。由於變得有條理,複雜性天然下降。
在實踐中能夠經過下面幾個途徑尋找高凝聚,首先是結構上的總體部分組成或組合關係,其次是計算的總步驟與子步驟之間的關係。固然還能夠從方法行爲方面來區分,因爲涉及某個單一職責功能的各個環節的方法函數,好比x+y+z這個計算職責涉及到了兩次加法函數,它們無疑是高凝聚在一塊兒。
同理,對於一個輸入輸出過程,涉及到一系列任務,無疑這些任務功能都高凝聚在一塊兒了。
瞭解了高凝聚的關係之後,其他剩餘的就是耦合了,這個時候組件之間的強關係就用耦合這個貶義詞表達。咱們的目的是保留下好的強關係也就是高凝聚,去除很差的強關係也就是低耦合。高凝聚,低耦合是咱們將低複雜性提升靈活性設計中重要的宗旨。
消息傳遞
咱們知道一個對象類是由字段屬性和方法行爲組成的,高凝聚低耦合的設計原則是要落實到這兩個基礎元素上。若是一個類(A)將另一個類(B)做爲本身的字段屬性,那麼咱們就認爲A和B的關係是一種組合性質的高凝聚關係,這種組合關係是一種總體由部分組成的強的結構性關係,也就是說,如同在數據表結構中確立外鍵關聯結構同樣,A和B之間的關係會伴隨A的誕生直至消亡,全生命週期陪伴。
除了靜態的結構關係之外,高凝聚低耦合還表達爲對象的方法行爲上,這須要經過分配職責來實現。什麼是職責?它包括三個方面:
- 對象應該執行的動做;
- 對象包含的知識如:算法 約束 規格 描述;
- 當前對象影響其餘對象的主要因素。
一個對象不能只有字段屬性和有關屬性的setter/getter,應該將業務職責分配給對象,使得對象有形有態。按照高凝聚(DDD聚合)原則分配。遵循假設:「若是沒有這個職責,會怎樣」。
以報童向買報人收錢這個案例爲例:
報童應該向顧客收取兩塊錢的買報費,他是直接把顧客的錢包拿過來從中掏出兩塊錢,仍是請求顧客本身從錢包裏掏出兩塊錢呢?無疑是後者,可是一般咱們是這麼寫代碼:
public class 報童{
Wallet theWallet = myCustomer.getWallet(); //報童從顧客手裏拿過錢包
if (theWallet.getTotalMoney() > 2.00) {
theWallet.subtractMoney(2.00);//報童直接從顧客錢包掏出兩塊錢
}
}
正常設計代碼應該以下:
public class 報童{
int paidAmount = myCustomer.getPayment(2.00);
if (paidAmount == 2.00) {
// say thank you
}
}
二者差異就是第一個錯誤設計師直接從顧客那裏拿出錢包myCustomer.getWallet();然後者只是告訴顧客你該掏出兩塊錢了myCustomer.getPayment(2.00)。
這二者的區別是什麼?Tell, Don't Ask原則:
報童只要告訴(Tell)顧客作什麼(付錢),而不直接參與怎麼作(Ask,你的錢夠嗎?夠才能買)。 報童只給顧客一個命令,而沒必要關注顧客是如何執行這個命令。
Tell, Don't Ask原則可以讓咱們保證兩個組件之間在動做上不會發生過於細節過多的耦合,而只是經過一個消息就能完成,經過發送消息告訴對方我須要什麼,而不是直接把對方拎過來搜身。至此,咱們已經完成了兩個組件之間最大鬆耦合,實現了架構設計的最大可能的靈活性。
總結:本文探討了軟件架構複雜性必然如天然界任何事物同樣不斷髮展,做爲軟件架構師如何學習大天然的巧妙設計,如同庖丁解牛同樣切分複雜性爲單一職責,再從結構和行爲的高凝聚關係方面進行組合或消息傳遞,從而真正實現高凝聚低耦合的靈活性目標。
http://www.jdon.com/artichect/flexable.html