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

huorongbj發表於2019-01-30

如果這是第二次看到我的文章, 歡迎 訂閱我的公眾號(跨界架構師)喲~ 

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

堅持原創,每一篇都是用心之作~

 

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

 

Z哥:@All 兄弟姐妹們,這次我這邊有個需求需要給「商品上架」增加一道稽核,會影響到大家和我互動的介面。大家抽空配合改一下,明天一起更新個版本。

 

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

 

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

 

作為被通知人,如果在你的現實工作中也發生了類似事件,我相信哪怕嘴上不說,心裡也會有不少想法和抱怨:“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

出處: https://www.cnblogs.com/Zachary-Fan/p/highcohesionlowcoupling.html

 

如果你喜歡這篇文章,可以點一下左下角的「 大拇指 」。

 

這樣可以給我一點反饋。: )

 

謝謝你的舉手之勞。

 

▶關於作者:張帆(Zachary,個人微訊號:Zachary-ZF)。堅持用心打磨每一篇高質量原創。

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

 

如果你是初級程式設計師,想提升但不知道如何下手。又或者做程式設計師多年,陷入了一些瓶頸想拓寬一下視野。歡迎關注我的公眾號「  跨界架構師  」,回覆「  技術  」,送你一份我長期收集和整理的思維導圖。 

如果你是運營,面對不斷變化的市場束手無策。又或者想了解主流的運營策略,以豐富自己的“倉庫”。歡迎關注我的公眾號「  跨界架構師  」,回覆「  運營  」,送你一份我長期收集和整理的思維導圖。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31544142/viewspace-2564934/,如需轉載,請註明出處,否則將追究法律責任。

相關文章