分佈式系統關注點——「高內聚低耦合」詳解

若是這是第二次看到個人文章,歡迎點擊文末連接訂閱個人我的公衆號(跨界架構師)喲~

本文長度爲3012字,建議閱讀8分鐘。html

堅持原創,每一篇都是用心之做~程序員


下面的這個場景你可能會以爲很熟悉(Z哥我又要出演了):json

Z哥:@All 兄弟姐妹們,此次我這邊有個需求須要給「商品上架」增長一道審覈,會影響到你們和我交互的接口。你們抽空配合改一下,明天一塊兒更新個版本。微信

小Y:哥,我這幾天很忙啊,昨天剛配合老王改過促銷!架構

小X:行~當一切已成習慣。less

做爲被通知人,若是在你的現實工做中也發生了相似事件,我相信哪怕嘴上不說,內心也會有很多想法和抱怨:「md,改的是你,我也要發佈,好冤啊!」。分佈式


這個問題的根本緣由就是多個項目之間的耦合度過於嚴重。工具

越大型的項目越容易陷入到這個昭潭中,難以自拔。架構設計

而解決問題的方式就是進行更合理的分層,而且持續保證分層的合理性。設計

一提到分層,必然離不開6個字「高內聚」和「低耦合」。


什麼是高內聚低耦合

在z哥以前的文章中有屢次提到,布式系統的本質就是「分治」和「冗餘」

其中,分治就是「分解 -> 治理 -> 歸併」的三部曲。「高內聚」、「低耦合」的概念就來源於此。

須要注意的是,當你在作「分解」這個操做的時候,務必要關注每一次的「分解」是否知足一個最重要的條件:不一樣分支上的子問題,不能相互依賴,須要各自獨立

由於一旦包含了依賴關係,子問題和父問題之間就失去了能夠被「歸併」的意義。

好比,一個「問題Z」被分解成了兩個子問題,「子問題A」和「子問題B」。可是,解問題A依賴於問題B的答案,解問題B又依賴於問題A的答案。這不就等於沒有分解嗎?

題外話:這裏的「如何更合理的分解問題」這個思路也能夠用到你的生活和工做中的任何問題上。


因此,當你在作「分解」的時候,須要有一些很好的着力點去切入。

這個着力點就是前面提到的「耦合度」和「內聚度」,二者是一個此消彼長的關係。

越符合高內聚低耦合這個標準,程序的維護成本就越低。爲何呢?由於依賴越小,各自的變動對其餘關聯方的影響就越小。

因此,「高內聚」和「低耦合」是咱們應當持續不斷追求的目標。

題外話:耦合度,指的是軟件模塊之間相互依賴的程度。好比,每次調用方法 A 以後都須要同步調用方法 B,那麼此時方法 A 和 B 間的耦合度是高的。

內聚度,指的是模塊內的元素具備的共同點的類似程度。好比,一個類中的多個方法有不少的共同之處,都是作支付相關的處理,那麼這個類的內聚度是高的。


怎麼作好高內聚低耦合

作好高內聚低耦合,思路也很簡單:定職責、作歸類、劃邊界

首先,定職責就是定義每個子系統、每個模塊、甚至每個class和每個function的職責。

好比,在子系統或者模塊層面能夠這樣。

又好比,在class或者function層面能夠這樣。

我想這點你們平時都會有意識的去作。

作好了職責定義後,內聚性就會有很大的提高,同時也提升了代碼/程序的複用程度。

至此,咱們才談得上「單一職責(SRP)」這種設計原則的運用。


其次,作歸類。梳理不一樣模塊之間的依賴關係。

像上面提到的案例1能夠歸類爲3層:

  1. 基礎層:商品基礎服務、會員基礎服務、促銷基礎服務

  2. 聚合層:購物車服務、商品詳情服務、登錄服務

  3. 接入層:快閃店API、綜合商城API

案例2也能夠歸類爲3層:

  1. 數據訪問層:訪問會員表數據、訪問會員積分表數據、訪問會員等級表數據

  2. 業務邏輯層:會員登錄邏輯、會員使用積分邏輯、會員升級邏輯

  3. 應用層:接收用戶輸入的帳戶密碼、接收用戶輸入的使用積分數、接收用戶的付款信息

最後就是劃邊界。好不容易梳理清楚,爲了不輕易被再次破壞,因此須要設立好合理清晰的邊界。

不然你想的是這樣整齊。

實際會慢慢變成這樣混亂。

那麼應該怎麼劃邊界呢?

class和function級別。這個層面能夠經過codereview或者靜態代碼檢測工具來進行,能夠關注的點好比:

  1. 調用某些class必須經過interface而不是implement

  2. 訪問會員表數據的class中不能存在訪問商品數據的function


模塊級別。能夠選擇如下方案:

  1. 給每一種類型的class分配不一樣project,打包到各自的dll(jar)中

  2. 每次代碼push上來的時候檢測其中的依賴是否有超出規定的依賴。例如,不能逆向依賴(檢測dal是否包含bll);不能在基礎層作聚合業務(檢測商品基礎服務是否包含其餘基礎服務的dll(jar))。


系統級別。及時識別子系統之間的調用是否符合預期,能夠經過接入一個調用鏈跟蹤系統(如,zipkin)來分析請求鏈路是否合法。


讓邊界更清晰、穩定的最佳實踐

不少時候不一樣的模塊或者子系統會被分配到不一樣的小組中負責,因此z哥再分享幾個最佳實踐給你。它可讓系統之間的溝通更穩定。


首先是:模塊對外暴露的接口部分,數據類型的選擇上儘可能作到寬進嚴出。好比,使用long代替byte之類的數據類型;使用弱類型代替強類型等等。

舉個「寬進嚴出」的例子:

//使用long代替byte之類的數據類型。

void Add(long param1, long param2){

    if(param1 <1000&& param2 < 1000){ //先接收進來,到裏面再作邏輯校驗。

    //do something...

    }

    else{

    //do something...

    }

}


其次是:寫操做接口,接收參數儘量少;讀操做接口,返回參數儘量多

爲何呢?由於不少時候,寫操做的背後會存在一個潛在預期,是「準確」。

準確度和可信度有着很大的聯繫,只有更多的邏輯處理在本身掌控範圍內進行才能越具有「可信度」(固然是職責範圍內的邏輯,而不是讓商品服務去計算促銷的邏輯)。反之,上游系統一個bug就會牽連到你的系統中。


而讀操做背後的潛在預期是:「知足」。你得提供給我知足我當前須要的數據,不然個人工做沒法開展。

可是呢,在不一樣時期,客戶端所須要的數據可能會發生變化,你沒法預測。因此呢,不要吝嗇,返回參數儘量多,用哪些,用不用是客戶端的事。

還能夠作的更好的一些,就是,在能夠知足的基礎上支持按需獲取。客戶端須要返回哪些字段本身經過參數傳過來,如此一來還能避免浪費資源作無用的數據傳輸。

題外話:對外露出的接口設計,可使用http + json 這種跨平臺 + 弱類型的技術組合,可具有更好的靈活性。


實際上,一個程序大多數狀況下,在某些時刻是客戶端,又在某些時刻是服務端。站在一個完整程序的角度來提煉參數設計的思路就是:吃」的要少,「產出」的要多

題外話:有一些設計原則能夠擴展閱讀一下。

單一職責原則SRP(Single Responsibility Principle)

開放封閉原則OCP(Open-Close Principle)

裏式替換原則LSP(the Liskov Substitution Principle LSP)

依賴倒置原則DIP(the Dependency Inversion Principle DIP)

接口分離原則ISP(the Interface Segregation Principle ISP)


總結

本文z哥帶你梳理了一下「高內聚低耦合」的本質(來自於哪,意義是什麼),而且分享了一些該怎麼作的思路。

能夠看到「高內聚」、「低耦合」其實沒有這個名字那麼高端。哪怕你如今正在工做的項目是一個單體應用,也能夠在class和function的設計中體會到「高內聚」、「低耦合」的奧妙。


來來來,接下去立刻開始在項目中「刻意練習」起來吧~



「易伸縮」篇的相關文章:



做者:Zachary

出處:www.cnblogs.com/Zachary-Fan…


若是你喜歡這篇文章,能夠點一下右下角的「大拇指」哦~。

這樣能夠給我一點反饋。: )

謝謝你的舉手之勞。


▶關於做者:張帆(Zachary,我的微信號:Zachary-ZF)。堅持用心打磨每一篇高質量原創。本文首發於公衆號:「跨界架構師」(ID:Zachary_ZF)。<-- 點擊後閱讀熱門文章

按期發表原創內容:架構設計丨分佈式系統丨產品丨運營丨一些思考。

若是你是初級程序員,想提高但不知道如何下手。又或者作程序員多年,陷入了一些瓶頸想拓寬一下視野。歡迎關注個人公衆號「跨界架構師」,回覆「技術」,送你一份我長期收集和整理的思惟導圖。

若是你是運營,面對不斷變化的市場一籌莫展。又或者想了解主流的運營策略,以豐富本身的「倉庫」。歡迎關注個人公衆號「跨界架構師」,回覆「運營」,送你一份我長期收集和整理的思惟導圖。

相關文章
相關標籤/搜索