【架構設計】保持簡單輕量設計的三個原則——DRY,KISS, YAGNI

JAVA旭陽發表於2022-12-29

前言

一個軟體輕量簡單的軟體架構是非常重要的,它可以讓我們花最小的代價就能滿足業務上的需求。那如何保證輕量簡單呢?那今天就和大家分享下這其中的秘密,也就是3個重要的指導原則,KISS原則,YAGNI原則和DRY原則,你們都知道並且理解嗎?

歡迎關注微信公眾號「JAVA旭陽」交流和學習

KISS原則

KISS原則, 英文全稱Keep it simple and stupid。核心思想就是儘量保持簡單

KISS原則指導我們在軟體設計的時候要儘量保持簡單,使用一些成熟的、適合業務的技術方案。另外從一個使用者的角度來思考,你設計時要思考如何讓自己的架構設計變得簡單,足夠易用,比如你開發的框架是不是對於接入成本低甚至0成本? 你設計的框架是否不侵入業務程式碼?

不僅軟體架構設計層面,在程式碼層面也處處要體現KISS原則。程式碼的可讀性和可維護性是衡量程式碼質量非常重要的兩個標準。而 KISS 原則就是保持程式碼可讀和可維護的重要手段。程式碼足夠簡單,也就意味著很容易讀懂,bug 比較難隱藏。即便出現 bug,修復起來也比較簡單。

我們來看下面校驗IP是否合法的3種實現方式哪個最“KISS”,大家覺得是哪個呢?

  1. 方式一

  1. 方式二

  1. 方式三

  • 方式一程式碼量最少,正規表示式本身是比較複雜的,寫出完全沒有 bug 的正則表達本身就比較有挑戰;另一方面,並不是每個程式設計師都精通正規表示式。對於不怎麼懂正規表示式的同事來說,看懂並且維護這段正規表示式是比較困難的。這種實現方式會導致程式碼的可讀性和可維護性變差,所以,從 KISS 原則的設計初衷上來講,這種實現方式並不符合 KISS 原則。
  • 方式二使用了 StringUtils 類、Integer 類提供的一些現成的工具函式,來處理 IP地址字串,邏輯清晰,可讀性好。
  • 方式三不使用任何工具函式,而是透過逐一處理 IP 地址中的字元,來判斷是否合法,容易出bug,不好理解。

小結: 綜合來看,第二種方式更符合KISS原則。並不是程式碼越少越好,還要考慮程式碼是否邏輯清晰、是否容易理解、是否夠穩定。

那如何寫出滿足 KISS 原則的程式碼?

  1. 不要使用同事可能不懂的技術來實現程式碼。比如前面例子中的正規表示式,還有一些程式語言中過於高階的語法等。
  2. 不要重複造輪子,要善於使用已經有的工具類庫。經驗證明,自己去實現這些類庫,出bug 的機率會更高,維護的成本也比較高。
  3. 不要過度最佳化。不要過度使用一些奇技淫巧(比如,位運算代替算術運算、複雜的條件語句代替 if-else、使用一些過於底層的函式等)來最佳化程式碼,犧牲程式碼的可讀性。

YAGNI原則

YAGNI 原則的英文全稱是:You Ain’t Gonna Need It。中文大意是說你根本不需要這麼做。核心思想就是不要過度設計

那什麼是過度設計呢?比如說你的應用,現在還是處在一個單體應用的階段,那麼這時候我就考慮啊,分散式事務該怎麼做,那或者說你的資料庫還是一個單庫的時候,我就在思考。如何去做資料異構,甚至是你的業務邏輯在並不複雜的階段的時候,你就再去思考如何去上一個工作流引擎,或者是規則引擎。那再比如現在圖形資料庫非常的火,那你就在想方設法的把圖形資料庫引入到你自己的專案當中。那這些是什麼呢啊?說好聽點叫面向簡歷程式設計,說難聽點就是什麼?脫褲子放屁,多此一舉。我們提倡面向未來的架構是什麼意思啊?是去規劃你未來技術發展的路線,當這一天需要到來的時候,你有能力去實現它。而不是說你現在就把未來不需要的東西去搬到自己的專案當中。

所以千萬不要為了技術而技術,一定要讓技術服務於業務,謹遵YAGNI原則,過度設計是災難。

DRY原則

DRY原則,英文全稱Don’t Repeat Yourself,直譯過來就是不要重複你自己。那這裡的重複是什麼意思?就是指程式碼一模一樣的才算重複嗎?實際不是的,我這裡從實現邏輯重複、功能語義重複和程式碼執行重複三個點來理解重複的意思。

  1. 實現邏輯重複

我們看下下面的例子:

  • 校驗使用者名稱

  • 校驗密碼

雖然上面兩個方法的程式碼邏輯是重複的,但是他們語義不重複,一個是用來校驗使用者名稱,一個是用來校驗密碼的,所以是符合DRY原則。如果我們強行把他們封裝成isValidUsernameOrPassword()方法,反而不符合單一職責原則,萬一哪天密碼的校驗邏輯變了怎麼辦呢?所以並不是說程式碼一樣就一定違反了DRY原則。反而有時候程式碼不一樣,恰好違反了DRY原則。

  1. 功能語義重複

直接上例子,專案中不同的同事由於不知道就寫了兩個校驗IP的方法。

  • 程式碼一

  • 程式碼二

儘管兩段程式碼的實現邏輯不重複,但語義重複,也就是功能重複,我們認為它違反了 DRY 原則。我們應該在專案中,統一一種實現思路,所有用到判斷 IP 地址是否合法的地方,都統一呼叫同一個函式。不然哪天校驗規則變了,很容易只改了其中一個,另外一個漏改,就會出現莫名其妙的bug。

  1. 程式碼執行重複

我們看下面一個登入的程式碼例子。

你有沒有發現email的校驗邏輯被執行了2次,這種重複執行的情況我們也可以認為違反了DRY原則。

說了這麼多,那有什麼好的辦法提高程式碼複用性,保證DRY原則呢?

  • 使用現成的輪子,不輕易造輪子

其實最關鍵的就是寫程式碼帶腦子,用到一個方法先看看有沒有現成的,不要看看不看,就動手在那裡造輪子。

  • 減少程式碼耦合

對於高度耦合的程式碼,當我們希望複用其中的一個功能,想把這個功能的程式碼抽取出來成為一個獨立的模組、類或者函式的時候,往往會發現牽一髮而動全身。移動一點程式碼,就要牽連到很多其他相關的程式碼。所以,高度耦合的程式碼會影響到程式碼的複用性,我們要儘量減少程式碼耦合。

  • 滿足單一職責原則

我們前面講過,如果職責不夠單一,模組、類設計得大而全,那依賴它的程式碼或者它依賴的程式碼就會比較多,進而增加了程式碼的耦合。根據上一點,也就會影響到程式碼的複用性。相反,越細粒度的程式碼,程式碼的通用性會越好,越容易被複用。

  • 模組化

這裡的“模組”,不單單指一組類構成的模組,還可以理解為單個類、函式。我們要善於將功能獨立的程式碼,封裝成模組。獨立的模組就像一塊一塊的積木,更加容易複用,可以直接拿來搭建更加複雜的系統。

  • 業務與非業務邏輯分離

越是跟業務無關的程式碼越是容易複用,越是針對特定業務的程式碼越難複用。所以,為了複用跟業務無關的程式碼,我們將業務和非業務邏輯程式碼分離,抽取成一些通用的框架、類庫、元件等。

  • 通用程式碼下沉

從分層的角度來看,越底層的程式碼越通用、會被越多的模組呼叫,越應該設計得足夠可複用。一般情況下,在程式碼分層之後,為了避免交叉呼叫導致呼叫關係混亂,我們只允許上層程式碼呼叫下層程式碼及同層程式碼之間的呼叫,杜絕下層程式碼呼叫上層程式碼。所以,通用的程式碼我們儘量下沉到更下層。

  • 繼承、多型、抽象、封裝

在講物件導向特性的時候,我們講到,利用繼承,可以將公共的程式碼抽取到父類,子類複用父類的屬性和方法。利用多型,我們可以動態地替換一段程式碼的部分邏輯,讓這段程式碼可複用。除此之外,抽象和封裝,從更加廣義的層面、而非狹義的物件導向特性的層面來理解的話,越抽象、越不依賴具體的實現,越容易複用。程式碼封裝成模組,隱藏可變的細節、暴露不變的介面,就越容易複用。

  • 應用模板等設計模式

一些設計模式,也能提高程式碼的複用性。比如,模板模式利用了多型來實現,可以靈活地替換其中的部分程式碼,整個流程模板程式碼可複用。

總結

本文和大家介紹了軟體工程領域中保持簡單架構設計的三個原則,其實所有的這些原則就是為了達到一個很簡單的目的,try everything to keep it simple,軟體設計上的簡單輕量是非常重要的。

歡迎關注微信公眾號「JAVA旭陽」交流和學習
更多學習資料請移步:程式設計師成神之路

相關文章