1. 程式人生 > >程式設計6大原則

程式設計6大原則

程式設計六大原則

2017年12月24日 22:57:20 LBJFxd 閱讀數:7603

 版權宣告:本文為博主原創文章,轉載請註明出處! https://blog.csdn.net/fanxudonggreat/article/details/78888267

1.單一職責

簡單來說單一職責就是一個類只負責一個功能。更加具體的說就是對一個類而言,應該是一組相關性很高的函式、資料的封裝,是高內聚低耦合的,對外界而言應該僅有一個引起它變化的原因。

單一職責在專案中的使用:

1.專案中的新手引導變數的管理可以統一在各自的Modle中用單獨的類來管理

2.MVP模式P層生命週期與V層生命週期的同步可以用單獨的包裝類來實現,

3.各種基礎框架功能的定義,例如:圖片的載入、快取、顯示等都應該在各自的類中去做。

下面以一個圖片載入庫的實現為例:成功載入一張圖片可分為:請求、快取、載入三個步驟,那我們就按照單一職責去建立三個類分別實現這三個功能

/**
 * 圖片顯示
 */
public class ImageLoad {

    public void displayImage(String url, ImageView imageView) {

    }

}

/**
 * 圖片快取
 */
public class ImageCache {

    public void put(String url,Bitmap bitmap){

    }

    public Bitmap get(String url){

    }

}

/**
 * 圖片載入
 */
public class ImageRequest {

    public Bitmap downloadImage(String url){

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

上面的例子:ImageLoad只負責顯示圖片,ImageRequest負責從網路下載圖片,ImageCache負責處理圖片快取邏輯,這樣設計各自的類職責相當單一,假如我們需要修改快取的邏輯,我們只需要修改ImageCache的邏輯,若專案升級我們需要升級網路請求庫,那涉及到圖片部分我們也只需要修改ImageRequest類即可。總之:單一職責所要表達的用意就是”單一”二字,但是如何劃分一個類、甚至是一個函式的職責,這就需要每一個開發者自己去設計。

2.開閉原則

開閉原則的英文全稱是Open Close Principle縮寫即OCP。開閉原則的定義是:軟體中的物件(類、模組、函式等)應該對於擴充套件是開放的,但是對於修改是封閉的。在軟體的生命週期內,因為變化、升級和維護等原因需要對軟體的原有程式碼進行修改時,可能會將錯誤的程式碼引入,從而破壞原有系統。因此當軟體需求發生變化時,我們應該儘量通過擴充套件的方式 來實現變化,而不是通過修改已有的程式碼。

開閉原則在專案中的使用:

1.基類與子類,子類可以繼承父類並擴充套件父類的功能

2.介面與實現類,介面定義功能,實現類按照各自的需求實現

繼續以上面的圖片載入框架為例:上面我們定義了三個類:ImageLoad,ImageRequest,ImageCache分別來做顯示、載入、快取這三件事,很簡單的滿足了單一職責的原則,但是當我們不同情況下需要執行不同的快取策略時是不是每次又要去修改ImageCache這個類?這是不是跟我們的開閉原則相違背?ImageRequest同樣如此。因此我們需要保證在新增新的快取策略時不需要修改原來的類,只需要在此基礎上擴充套件就可以了,這樣就可以避免修改原來的類引起的未知的錯誤。

/**
 * 圖片顯示
 */
public class ImageLoad {

    public void displayImage(String url, ImageView imageView) {

    }

}

/**
 * 圖片快取
 */
public interface ImageCache {

    public void put(String url,Bitmap bitmap);

    public Bitmap get(String url);

}

/**
 * 圖片載入
 */
public interface ImageRequest {

    public Bitmap downloadImage(String url);

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

注意:我們只是將ImageCache類和ImageRequest類改成了介面,這樣的好處就是我們需要新增新的快取策略時只需要實現ImageCache介面,ImageRequest同樣如此。下面我們實現記憶體快取和SD卡快取

/**
 * 記憶體快取
 */
public class MemoryCache implements ImageCache {

    @Override
    public void put(String url, Bitmap bitmap) {

    }

    @Override
    public Bitmap get(String url) {
        return null;
    }
}

/**
 * SD卡快取
 */
public class DiskCache implements ImageCache {

    @Override
    public void put(String url, Bitmap bitmap) {

    }

    @Override
    public Bitmap get(String url) {
        return null;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

然後在我們的ImageLoad中可以動態注入ImageCahce實現:

/**
 * 圖片顯示
 */
public class ImageLoad {

    private ImageCache mCache = new MemoryCache();//預設快取策略為記憶體快取

    /**
     * 快取策略注入
     */
    public void setImageCache(ImageCache imageCache) {
        this.mCache = imageCache;
    }

    public void displayImage(String url, ImageView imageView) {

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在上面的例子中,通過setImageCache方法開發者可以對ImageLoad注入不同的快取實現,這使得ImageLoad更加簡單、健壯、擴充套件性、靈活性更高,同時也使得ImageCache的新增簡單高效(只需要實現ImageCache介面然後通過setImageCache注入),並且這些擴充套件不會不會導致ImageLoad類修改。這正是開閉原則的核心思想:對擴充套件開發,對修改封閉。

3.里氏替換原則

里氏替換原則的定義:如果對每一個型別為S的物件O1,都有型別為T的物件O2,程式P在所有的物件O1都帶換成O2時,程式P的行為沒有發生變化,那麼型別S是型別T的子型別換言之就是所有引用基類的地方必須能透明的使用其子類的物件。更通俗的講就是隻要父類出現的地方子類就可以出現,而且替換為子類也不會產生任何的錯誤或者異常。

里氏替換原則的核心是抽象,而抽象又依賴於繼承這個特性,在OOP當中,繼承的優缺點都相當明顯。

優點:

1.程式碼重用,減少建立類的成本,每個子類都擁有父類的方法和屬性

2.子類與父類基本相似,但又與父類有所區別

3.提高程式碼的可擴充套件性

缺點:

1.繼承是侵入性的,只要繼承就必須擁有父類的方法和屬性

2.可能造成子類程式碼冗餘,靈活性降低,因為子類必須擁有父類的屬性和方法

在上面的例子中,我們通過ImageCache建立起了一套快取的規範,在通過setImageCache注入不同的具體實現,保證了系統的擴充套件性和靈活性。因此開閉原則和里氏替換原則往往是生死相依,形影不離的,通過里氏替換原則來達到對擴充套件開放,對修改關閉的效果。

4.依賴倒置原則

依賴倒置原則指定了一種特定的解耦形式,使得高層次的模組不依賴與低層次模組的實現細節的目的,依賴模組被顛倒了。依賴倒置原則有以下幾個關鍵點:

1.高層模組不應該依賴於低層模組,兩者都應該依賴其抽象

2.抽象不應該依賴於細節

3.細節應該依賴於抽象

在Java語言中,抽象就是指介面或者抽象類,二者都是不能夠被直接例項化的:細節就是實現類,實現介面或者抽象類而產生的類就是細節,其特點就是可以直接被例項化,也就是可以使用關鍵字new產生一個物件。高層模組就是指呼叫端,底層模組就是指具體的實現類。依賴倒置原則在Java語言中的表現就是:模組間的依賴通過抽象產生,實現類之間不發生直接的依賴關係,其依賴關係是通過介面或者抽象類產生的。使用一句話概括就是:面向介面程式設計或者說是面向抽象程式設計。

如果類與類直接依賴細節,那麼這幾個類之間就有直接的耦合,當具體的需求發生變化是,意味著同時修改依賴者的程式碼。在上面圖片載入的例子中,ImageLoad這個類依賴於ImageCache這個介面,而具體的實現可以通過setImageCache注入,這樣當Cache策略需要升級時,只需要實現ImageCache介面,然後通過setImageCache注入到ImageLoad中。這樣就保證了細節與依賴的隔離。

5.介面隔離原則

介面隔離原則的定義是:客戶端不應該依賴於他不需要的介面。另一種定義是:類之間的依賴關係應該建立在最小的介面上。介面隔離原則將非常龐大,臃腫的介面拆分成更小的和更具體的介面,這樣客戶端將會值需要知道它們感興趣的方法。介面隔離原則的目的是系統解開耦合,從而容易重構、更改和部署。

在上面的IamgeLoad例子中,ImageCache指向ImageLoad提供了get和put方法,其他一概不管,這使得具體的快取策略實現對ImageLoad隱藏,這就是使用最小化介面隔離了實現類的細節,也促使我們將更加龐大的介面拆分到更細粒度的介面當中,這同樣使得我們的系統具有更低的耦合性、更高的靈活性。

6.迪米特原則

迪米特原則:一個物件應該對其他物件有最少的瞭解,通俗的講,一個類應該對自己需要耦合或呼叫的類知道的最少,類的內部如何實現與呼叫者或者依賴者沒有關係,呼叫者或者依賴者只需要知道他需要的方法即可,其他的一概不管。類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。

在上面的圖片載入框架設計中,我們的IamgeLoad只依賴於ImageCache的get和put方法,對其內部實現細節一無所知,即使通過setImageCache改變注入的物件時,ImageLoad也是對其內部實現細節不關心的,ImageLoad只關心ImageCache提供的get和put方法。這樣使得系統具有更加低的耦合性和更好的擴充套件性。

小結:

在應用開發過程中,最難的不是完成應用的開發工作,而是在後續的升級、維護過程中讓應用系統能夠擁抱變化。擁抱變化也意味著在滿足需求而且不破壞系統穩定的前提下保持高可擴充套件性、高內聚、低耦合,在經歷了各版本的變更之後依然保持著清晰、靈活、穩