架構設計原則

架構設計原則

如果一個技術已經存在2年,比如現在很火的前端技術react和vue等,那麼我們能預估這個技術大致還有2年的生命期,再久就不確定了;如果一個架構或設計原則已經存在15年,例如單一職責和依賴倒置原則,我可以預期它還有15年甚至更久的生命期。原則比具體技術更抽象,更接近事物本質,也更經得起時間考驗的東西。這些原則沉澱在架構師的腦海中,最終內化成他的思維模式,以潛意識方式影響和指導他的架構和設計工作。

SOLID 面向對象設計原則


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

架構設計原則

原文:There should never be more than one reason for a class to change.

譯文:修改某個類的理由應該只有一個,如果超過一個,說明類承擔不止一個職責,要視情況拆分

理解:對於一個類而言,應該僅有一個引起它變化的原因。說白了就是,不同的類具備不同的職責,各施其責。這就好比一個團隊,大家分工協作,互不影響,各做各的事情。

二、開閉原則(Open Closed Principle - OCP)

架構設計原則

原文:Software entities like classes, modules and functions should be open for extension but closed for modifications.

譯文:軟件實體應該對擴展開放,對修改封閉

理解:設計時要儘可能抽象出不變和易變的部分,當需求有改動,要修改代碼了,此時您要做的是,儘量用繼承或組合的方式來擴展類的功能,而不是直接修改類的代碼。當系統的架構和設計滿足當足的業務,我們應該儘可能採用開閉原則;如果業務發生本質變更,設計已經不再適用了,那別猶豫,直接修改吧。

三、里氏替代原則(Liskov Substitution Principle - LSP)

架構設計原則

原文:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

譯文:使用基類的指針或引用的函數,必須是在不知情的情況下,能夠替換成派生類的對象

四層含議:

  • 子類可以實現父類的抽象方法,但是不能覆蓋父類的非抽象方法。
  • 子類中可以增加自己特有的方法。
  • 當子類覆蓋或實現父類的方法時,方法的前置條件(即方法的入參)要比父類方法的輸入參數更寬鬆。
  • 當子類的方法實現父類的抽象方法時,方法的後置條件(即方法的返回值)要比父類更嚴格。

疑問:里氏替換原則要求子類避免重寫父類方法非抽象方法,而多態卻是要求子類重寫父類的方法。不明白里氏替換原則與多態之間的取捨。

四、最少知識原則(Least Knowledge Principle - LKP)

原文:Only talk to you immediate friends.

理解:這個很好理解,軟件設計時應該儘可能的做到低偶合,系統間、模塊間、類之間要追求最小化依賴。

最少知識原則(Least Knowledge Principle - LKP)也叫:迪米特法則(Law of Demeter)

五、接口隔離原則(Interface Segregation Principle - ISP)

架構設計原則

原文:The dependency of one class to another one should depend on the smallest possible interface.

譯文:不要強迫用戶去依賴它們不使用的接口。換句話說,使用多個專門的接口比使用單一的大而全接口要好

理解:客戶端不應該依賴它不需要的接口,類間的依賴關係應該建立在最小的接口上。

六、依賴倒置原則(Dependence Inversion Principle - DIP)

架構設計原則

原文:High level modules should not depends upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

譯文:高層模塊不應該依賴於低層模塊,它們應該依賴於抽象。抽象不應該依賴於細節,細節應該依賴於抽象

理解:應該面向接口編程,不應該面向實現類編程。面向實現類編程,相當於就是論事,那是正向依賴(正常人思維);面向接口編程,相當於通過事物表象來看本質,那是反向依賴,即依賴倒置(程序員思維)。

例如一個web應用內,Controller是上層模塊,Service是接口抽象,ServiceImpl是低層實現。Controller依賴Service來完成業務邏輯時,而ServiceImpl需要實現Service,Controller和ServiceImpl都依賴Service,即高層和低層實現都依賴抽象;高層模塊不應該依賴於低層實現,即我們不應該在Controller層直接通過ServiceImpl來處理業務邏輯。

其它設計原則


組合/聚合複用原則(Composition/Aggregation Reuse Principle - CARP)

當要擴展類的功能時,優先考慮使用組合,而不是繼承。

這條原則在23種經典設計模式中頻繁使用,如:代理模式、裝飾模式、適配器模式、策略模式、狀態模式等,可見江湖地位非常之高!

無環依賴原則(Acyclic Dependencies Principle - ADP)

架構設計原則

當A模塊依賴於B模塊,B模塊依賴於C模塊,C依賴於A模塊,此時將出現循環依賴。

在設計中應該避免這個問題,可以分析各個模式,區分出依賴與被依賴的部分。

好萊塢原則(Hollywood Principle - HP)

好萊塢明星的經紀人一般都很忙,他們不想被打擾,往往會說:Don’t call me, I’ll call you. 翻譯為:不要聯繫我,我會聯繫你。

對應於軟件設計而言,最著名的就是“控制反轉”(或稱為“依賴注入”),我們不需要在代碼中主動的創建對象,而是由容器幫我們來創建並管理這些對象。

不要重複你自己(Don’t repeat yourself - DRY)

不要讓重複的代碼到處都是,要讓它們足夠的重用,所以要儘可能地封裝。

DRY的核心思想不僅僅是指代碼重複,更多的是指業務邏輯處理重複或類似。

保持它簡單與傻瓜(Keep it simple and stupid - KISS)

不要讓系統變得複雜,界面簡潔,功能實用,操作方便,要讓它足夠的簡單,足夠的傻瓜。

高內聚與低耦合(High Cohesion and Low Coupling - HCLC)

模塊內部需要做到內聚度高,模塊之間需要做到耦合度低。

慣例優於配置(Convention over Configuration - COC)

儘量讓慣例來減少配置,這樣才能提高開發效率,儘量做到“零配置”。很多開發框架都是這樣做的。

想想使用Maven、Springboot後,比起原來我們的幸福指數上升了多少!

命令查詢分離(Command Query Separation - CQS)

在定義接口時,要做到哪些是命令,哪些是查詢,要將它們分離,而不要揉到一起。

符合CQS的設計,系統的後期伸縮會比較好做。

關注點分離(Separation of Concerns - SOC)

將一個複雜的問題分離為多個簡單的問題,然後逐個解決這些簡單的問題,那麼這個複雜的問題就解決了。難就難在如何進行分離。

契約式設計(Design by Contract - DBC)

模塊或系統之間的交互,都是基於契約(接口或抽象)的,而不要依賴於具體實現。該原則建議我們要面向契約編程。

你不需要它(You aren’t gonna need it - YAGNI)

不要一開始就把系統設計得非常複雜,不要陷入“過度設計”的深淵。應該讓系統足夠的簡單,而卻又不失擴展性,這是其中的難點。

YAGNI感覺和KISS是同一個原則。

15 條架構原則


N + 1 設計

永遠不要少於兩個,通常為三個。比方說無狀態的 Web API,一般部署至少 >= 2 個。

回滾設計

確保系統可以回滾到以前發佈過的任何版本。可以通過發佈系統保留歷史版本,或者代碼中引入動態開關切換機制 (Feature Switch)。

禁用設計

能夠關閉任何發佈的功能。新功能隱藏在動態開關機制 (Feature Switch) 後面,可以按需一鍵打開,如發現問題隨時關閉禁用。

監控設計

在設計階段就必須考慮監控,而不是在實施完畢之後補充。例如在需求階段就要考慮關鍵指標監控項,這就是度量驅動開發 (Metrics Driven Development) 的理念。

設計多活數據中心

不要被一個數據中心的解決方案把自己限制住。當然也要考慮成本和公司規模發展階段。

使用成熟的技術

只用確實好用的技術。商業組織畢竟不是研究機構,技術要落地實用,成熟的技術一般坑都被踩平了,新技術在完全成熟前一般需要踩坑躺坑。

異步設計

能異步儘量用異步,只有當絕對必要或者無法異步時,才使用同步調用。

無狀態系統

儘可能無狀態,只有當業務確實需要,才使用狀態。無狀態系統易於擴展,有狀態系統不易擴展且狀態複雜時更易出錯。

水平擴展而非垂直升級

永遠不要依賴更大、更快的系統。一般公司成長到一定階段普遍經歷過買更大、更快系統的階段,即使淘寶當年也買小型機扛流量,後來扛不住才體會這樣做不 scalable,所以才有後來的去 IOE 行動。

設計時至少要有兩步前瞻性

在擴展性問題發生前考慮好下一步的行動計劃。架構師的價值就體現在這裡,架構設計對於流量的增長要有提前量。

非核心則購買

如果不是你最擅長,也提供不了差異化的競爭優勢則直接購買。避免 Not Invented Here 症狀,避免凡事都要重造輪子,畢竟達成業務目標才是重點。

使用商品化硬件

在大多數情況下,便宜的就是最好的。這點和第 9 點是一致的,通過商品化硬件水平擴展,而不是買更大、更快的系統。

小構建、小發布和快試錯

全部研發要小構建,不斷迭代,讓系統不斷成長。這個和微服務理念一致。

隔離故障

實現故障隔離設計,通過斷路保護避免故障傳播和交叉影響。通過艙壁泳道等機制隔離失敗單元 (Failure Unit),一個單元的失敗不至影響其它單元的正常工作。

自動化

設計和構建自動化的過程。如果機器可以做,就不要依賴於人。自動化是 DevOps 的基礎。

微服務的4個設計原則


一、AKF擴展立方體(Scalability Cube)

架構設計原則

《架構即未來》一書中提出的可擴展模型,這個立方體有三個軸線,每個軸線描述擴展性的一個維度:

X軸:指的是水平復制,比如採用集群的方式提升應用的負載能力、採用冗餘的方式實現數據的讀寫分離,提升存諸的查詢能力等等。

Y軸:指的是數據分片,主要用於數據存儲。比如採取分庫、分表的方式提升存儲的最大訪問能力、縮短訪問時間等等。

Z軸:指的是功能拆分。當應用變得臃腫時,把功能拆分成一個個獨立的小系統,更利於整體的把控。

二、前後端分離

架構設計原則

前後端分離原則,簡單來講就是前端和後端的從代碼到部署都分離開來。不要繼續以前的服務端模板技術,比如JSP ,把Java、JS、HTML、CSS 都堆到一個頁面裡,稍複雜的頁面就無法維護。這種分離模式的方式有幾個好處:

  • 前後端技術分離,可以由各自的專家來對各自的領域進行優化,這樣前端的用戶體驗優化效果會更好。
  • 分離模式下,前後端交互界面更加清晰,就剩下了接口和模型,後端的接口簡潔明瞭,更容易維護。
  • 前端多渠道集成場景更容易實現,後端服務無需變更,採用統一的數據和模型,可以支撐前端的web UI、移動App等訪問。

三、無狀態服務

架構設計原則

如果一個數據需要被多個服務共享,才能完成一筆交易,那麼這個數據被稱為狀態。進而依賴這個“狀態”數據的服務被稱為有狀態服務,反之稱為無狀態服務。

那麼這個無狀態服務原則並不是說在微服務架構裡就不允許存在狀態,表達的真實意思是要把有狀態的業務服務改變為無狀態的計算類服務,那麼狀態數據也就相應的遷移到對應的“有狀態數據服務”中。例如我們以前在本地內存中建立的數據緩存、Session緩存,到現在的微服務架構中就應該把這些數據遷移到分佈式緩存中存儲,讓業務服務變成一個無狀態的計算節點。遷移後,就可以做到按需動態伸縮,微服務應用在運行時動態增刪節點,就不再需要考慮緩存數據如何同步的問題。

四、Restful通訊風格

架構設計原則

無狀態協議HTTP,具備先天優勢,擴展能力很強。例如需要安全加密是,有現成的成熟方案HTTPS可用。JSON報文序列化,輕量簡單,人與機器均可讀,學習成本低,搜索引擎友好。語言無關,各大熱門語言都提供成熟的Restful API框架,相對其他的一些RPC框架生態更完善。


分享到:


相關文章: