若是這是第二次看到個人文章,歡迎點擊文末連接訂閱個人我的公衆號(跨界架構師)喲~
本文長度爲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層:
基礎層:商品基礎服務、會員基礎服務、促銷基礎服務
聚合層:購物車服務、商品詳情服務、登錄服務
接入層:快閃店API、綜合商城API
案例2也能夠歸類爲3層:
數據訪問層:訪問會員表數據、訪問會員積分表數據、訪問會員等級表數據
業務邏輯層:會員登錄邏輯、會員使用積分邏輯、會員升級邏輯
應用層:接收用戶輸入的帳戶密碼、接收用戶輸入的使用積分數、接收用戶的付款信息
最後就是劃邊界。好不容易梳理清楚,爲了不輕易被再次破壞,因此須要設立好合理清晰的邊界。
不然你想的是這樣整齊。
實際會慢慢變成這樣混亂。
那麼應該怎麼劃邊界呢?
class和function級別。這個層面能夠經過codereview或者靜態代碼檢測工具來進行,能夠關注的點好比:
調用某些class必須經過interface而不是implement
訪問會員表數據的class中不能存在訪問商品數據的function
模塊級別。能夠選擇如下方案:
給每一種類型的class分配不一樣project,打包到各自的dll(jar)中
每次代碼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)。<-- 點擊後閱讀熱門文章
按期發表原創內容:架構設計丨分佈式系統丨產品丨運營丨一些思考。
若是你是初級程序員,想提高但不知道如何下手。又或者作程序員多年,陷入了一些瓶頸想拓寬一下視野。歡迎關注個人公衆號「跨界架構師」,回覆「技術」,送你一份我長期收集和整理的思惟導圖。
若是你是運營,面對不斷變化的市場一籌莫展。又或者想了解主流的運營策略,以豐富本身的「倉庫」。歡迎關注個人公衆號「跨界架構師」,回覆「運營」,送你一份我長期收集和整理的思惟導圖。