設計模式之高質量代碼

公號:碼農充電站pro
主頁:https://codeshellme.github.iojava

若是有人問你,「什麼樣的代碼是好代碼」,你會怎樣回答呢?git

0,什麼是高質量代碼

我以爲回答這個問題,應該從兩個方面考慮。github

  1. 從業務角度考慮。首先,在公司開發一款軟件,應該是業務在驅動。因此,從這個角度來講,代碼第一個應該知足的是業務需求,若是連最基本的業務需求都知足不了,別的也就無從談起。
  2. 從純代碼層面考慮。本篇咱們重點要介紹的也就是這個問題。

那從純代碼層面來講,什麼樣的代碼纔是好代碼呢?算法

一般會有如下幾個判斷標準:shell

  • 可維護性:在當前代碼的基礎上,作修正或改進,是否容易作到?
  • 可擴展性:當有了新的需求,在不對當前代碼作大的改動的前期下,是否容易知足?
  • 可複用性:代碼是否能較容易的遷移到別的地方使用?不重複造輪子。
  • 可讀性:當其餘人閱讀代碼,或者過一段時間本身再閱讀,是否容易理解?
  • 靈活性:是否足夠靈活,易調整?
  • 簡潔性:是否簡單,不復雜?
  • 可測試性:是否容易測試正確性?

好的代碼,不必定要知足以上全部的條件。但一條也不知足的代碼,基本上就不是好代碼了。編程

1,如何編寫高質量代碼

無規則不成方圓,寫代碼也是如此。要寫出好的代碼,須要遵照一些規則,主要有如下幾個方面:設計模式

  • 設計原則
  • 設計模式
  • 編碼規範
  • 持續重構

1.1,設計原則

每種設計模式都遵照了一個或多個設計原則。經常使用的設計原則有如下幾種:安全

  • 單一職責原則:一個類的職責要單一明確。
  • 開閉原則:代碼應該對擴展開發,對修改關閉(儘可能減小對原有代碼的修改)。
  • 裏式替換原則:可以使用父類對象的地方,也能使用子類。
  • 接口隔離原則:接口的使用者不該該被強迫依賴它不須要的接口。
  • 依賴倒置原則:高層模塊不要依賴低層模塊。高層模塊和低層模塊應該經過抽象來互相依賴。抽象不要依賴具體實現細節,具體實現細節依賴抽象。
  • KISS 原則:儘可能保持代碼簡單。
  • YAGNI 原則:不要編寫當前用不到的功能/代碼,不要作過分設計。但並非不須要考慮代碼的擴展性。
  • DRY 原則:避免重複性代碼。
  • LOD 原則:最小知識原則,每一個模塊只關心與本身關係密切的模塊的有限知識。

另外在面向對象編程中,也有兩個比較重要的編程原則:架構

  • 基於接口而非實現編程:設計接口的時候要自頂向下,統攬全局,不拘泥於細節。
  • 多用組合少用繼承:繼承的最大問題在於,當繼承層次過深層,過於複雜,就會影響到代碼的可讀性和維護性。

在編碼的過程當中,要時常想着這些原則,思考本身的代碼是否符合其中的某項或多項原則。併發

1.2,設計模式

常見的設計模式有23 種,下面會詳細介紹。

1.3,編碼規範

編碼規範注重的是代碼細節,主要目的是讓代碼具備可讀性。總體上來講,好的代碼,對外應該有一個統一的代碼風格,代碼風格不必定有好壞之分,但必定有是否統一之別。

另外,代碼命名也很重要,大到項目命名,目錄命名,包名等。小到類名,接口名,方法名,對象名,變量名等。命名最基本的要求是用詞標準達意,讓人一看知道大概的用途是什麼。

還有,必要的地方要有必要的註釋,對於他人及本身回頭看代碼都有幫助。

1.4,持續重構

隨着項目需求的增長變化,代碼結構,代碼量也都會跟着變化。代碼重構須要咱們不斷的從總體架構的角度審視整個項目代碼的結構,是否已經變得混亂無序。只有不斷的對代碼進行重構,才能使得代碼持續的具備可維護性,可讀性等標準。

2,如何發現代碼的問題

通過上文,咱們已經知道了高質量代碼的標準是什麼。那麼,當咱們編寫完一部分代碼後,應該怎樣判斷本身寫的代碼是不是高質量呢?

文章開頭已經提到過,好的代碼應該從業務和純代碼兩個角度來衡量,下面咱們就從這兩個角度來看,通常要對代碼作哪些檢查?

業務角度:

  • 代碼可否知足業務需求(邏輯是否正確,是否有bug)?
  • 代碼是否健壯,可否應對邊界條件(特殊狀況)?
  • 軟件性能是否足夠,算法是否最優?
  • 代碼是否有線程安全問題,可以支持併發?
  • 是否具有事物安全?

從純代碼角度考慮:

  • 代碼結構,目錄劃分是否清晰合理?
  • 是否知足可維護性,可擴展性等標準?
  • 是否遵循設計原則?是否過渡設計?
  • 是否遵照代碼規範,風格是否統一?
  • 有無必要使用設計模式,運用是否得當?
  • 有無單元測試,測試是否全面?

當咱們完成某一階段的代碼後,能夠嘗試從以上幾點來檢查代碼是否過關。

下面主要介紹設計模式。

3,設計模式

設計模式講的是如何編寫可擴展、可維護、可讀的高質量代碼,它是針對軟件開發中常常遇到的一些設計問題,總結出來的一套通用的解決方案。

使用設計模式,可使得咱們編寫的代碼具備一個良好的結構,從而寫出優雅的代碼。設計模式關注的是,類與類之間以及對象與對象之間如何交互。

常見的設計模式有23 種,但並非每一種模式都經常使用。這23 種設計模式可分爲3 大類,分別是:

  • 建立型:用於解決對象的建立問題。
  • 結構型:用於處理類或對象之間的組合關係。
  • 行爲型:用於處理類或對象之間怎樣交互及分配職責的問題。

下面介紹每類之中都包含哪些設計模式。

3.1,建立型

建立型包含5 種設計模式:

  • 單例模式:保證一個類只能有一個實例,並提供一個全局訪問點。
  • 工廠方法:定義了一個建立對象的接口,由子類決定實例化哪個類,使得類的實例化推遲到子類中。
    • 簡單工廠:嚴格來講不是一種模式,但常常用於封裝建立對象的過程。
  • 抽象工廠:提供了建立一系列相關對象的接口,而無需指定它們具體的類。
  • 建立者模式:將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。
  • 原型模式(不經常使用):用原型實例指定建立對象的種類,而且經過拷貝這個原型來建立新的對象。

3.2,結構型

結構型包含7 種設計模式:

  • 裝飾者模式:動態的給一個對象添加其它功能。從擴展性來講,這種方式比繼承更有彈性,更加靈活,可做爲替代繼承的方案。
  • 適配器模式:將一個類的接口轉換成客戶指望的另外一個接口,使得本來接口不兼容的類能夠相互合做。
  • 代理模式:爲對象提供一個代理,來控制對該對象的訪問。
  • 橋接模式:將抽象部分與它的實現部分分離,使它們能夠獨立的變化。
  • 組合模式(不經常使用):能夠將對象組合成樹形結構來表示「總體-部分」的層次結構,使得客戶能夠用一致的方式處理個別對象和對象組合。
  • 外觀模式(不經常使用):提供了一個統一的接口,用來訪問子系統中的一羣接口。它定義了一個高層接口,讓子系統更容易使用。
  • 享元模式(不經常使用):「享元」即共享單元,當一個系統中出現大量重複對象的時候,將對象設計成享元,以減小內存中對象的數量,節省內存。

3.3,行爲型

行爲型包含11 種設計模式:

  • 觀察者模式:定義了對象之間的一對多關係,以便當一個對象的狀態發生變化時,全部依賴它的對象都能獲得通知,並自動更新。
  • 策略模式:定義一系列的算法,將它們一個個封裝起來,讓它們之間能夠互相替換。可以使得算法的變化獨立於使用它的客戶。
  • 迭代器模式:提供一個方法,能夠順序訪問集合中的元素,而不暴露集合的內部表示。
  • 模板模式:在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中,使得子類能夠在不改變算法結構的狀況下,從新定義算法中的某些步驟。
  • 狀態模式:容許對象在內部狀態改變時,改變它的行爲,對象看起來好像改變了它的類。
  • 職責鏈模式:多個對象能夠處理同一個請求,這些對象連成一條鏈,並沿着這條鏈傳遞這個請求,直到有一個對象處理它。
  • 訪問者模式(不經常使用):表示一個做用於某對象結構中的各元素的操做。它使你能夠在不改變各元素的類的前提下,定義做用於這些元素的新操做。
  • 命令模式(不經常使用):將「請求」封裝成對象,以便使用不一樣的請求,隊列或日誌來參數化其它對象。另外還支持可撤銷的操做。
  • 備忘錄模式(不經常使用):在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。以便之後恢復該對象。
  • 中介模式(不經常使用):用一箇中介對象來封裝一系列的對象交互,從而最小化對象之間的交互關係,下降代碼複雜度。
  • 解釋器模式 (不經常使用):爲某個語言定義它的語法表示,並定義一個解釋器,來解釋這種語法。

4,UML 建模

設計模式中常常會用到UML 圖來表示類與類之間的關係,下面介紹6 種UML 規定的類關係,分別是:

  • 泛化
  • 實現
  • 聚類
  • 組合
  • 關聯
  • 依賴

各類關係之間的強弱性爲:

  • 泛化 = 實現 > 組合 > 聚合 > 關聯 > 依賴。
  • 泛化與實現表示的關係最強。
  • 依賴表示的關係最弱。

4.1,泛化關係

泛化可理解爲繼承關係。以下代碼中,類B繼承了類A。

public class A { ... }
public class B extends A { ... }

泛化關係用一個實線三角箭頭表示,關係圖以下,箭頭由B 指向A,表示B繼承了A:

在這裏插入圖片描述

4.2,實現關係

實現類和接口之間的關係,稱爲實現關係。

以下代碼中,類B 實現了接口A。

public interface A {...}
public class B implements A { ... }

實現關係用一個虛線三角箭頭表示,關係圖以下,箭頭由B 指向A,表示B 實現了A:

在這裏插入圖片描述

4.3,聚合關係

聚合是一種包含關係,A 類對象包含B 類對象,B類對象的生命週期能夠不依賴A 類對象的生命週期。

以下代碼中,A 類對象包含了B 類對象。

public class A {
  private B b;
  public A(B b) {
    this.b = b;
  }
}

聚合關係用一個實線空心菱形箭頭表示,關係圖以下,箭頭由B 指向A,表示A 中聚合了B:

在這裏插入圖片描述

4.4,組合關係

組合是一種包含關係。A 類對象包含B 類對象,B 類對象的生命週期依賴A 類對象的生命週期,B 類對象不可單獨存在。

以下代碼中,A 類對象包含了B 類對象。

public class A {
  private B b;
  public A() {
    this.b = new B();
  }
}

組合關係用一個實線實心菱形箭頭表示,關係圖以下,箭頭由B 指向A,表示A 中組合了B:

在這裏插入圖片描述

4.5,關聯關係

關聯是一種很是弱的關係,包含聚合、組合兩種關係。若是B 類對象是A 類的成員變量,那麼B 類和A 類就是關聯關係。

以下兩種代碼都是關聯關係。

public class A {
  private B b;
  public A(B b) {
    this.b = b;
  }
}

// 或者
public class A {
  private B b;
  public A() {
    this.b = new B();
  }
}

關聯關係用一個實線箭頭表示,關係圖以下,箭頭由A 指向B,表示A 關聯B:

在這裏插入圖片描述

4.6,依賴關係

依賴是一種比關聯關係更弱的關係,包含關聯關係。只要B 類對象和A 類對象有任何使用關係。

以下三種代碼都是依賴關係。

public class A {
  private B b;
  public A(B b) {
    this.b = b;
  }
}
// 或者
public class A {
  private B b;
  public A() {
    this.b = new B();
  }
}
// 或者
public class A {
  public void func(B b) { ... }
}

依賴關係用一個虛線箭頭表示,關係圖以下,箭頭由A 指向B,表示A 依賴B:

在這裏插入圖片描述

本篇文章主要介紹了什麼是高質量代碼,如何發現代碼中的問題,以及如何編寫高質量代碼。另外重點介紹了23 種設計模式都有哪些,及每種設計模式的含義。最後介紹了設計模式中常常用到的UML 關係都有哪些。

在這裏插入圖片描述

但願本篇文章對你有所幫助,若是哪裏有任何問題也歡迎指正,謝謝!

(完。)


歡迎關注做者公衆號,獲取更多技術乾貨。

在這裏插入圖片描述

相關文章
相關標籤/搜索