面向對象設計 依賴倒置原則(DIP)

Dependency inversion principle

簡介

主目錄:一個面向對象設計(OOD)的學習思路設計java

DIP.png

引入: 高層的決定不能由於某一個低層次模塊的變更而影響全局,致使整個系統的變更。編程

什麼是DIP?

  • 全稱:依賴倒置原則(Dependency inversion principle)
  • 定義:
  1. 高層次的模塊不該該依賴於低層次的模塊,二者都應該依賴於抽象接口
  2. 抽象接口不該該依賴於具體實現,而具體實現則因該依賴於抽象接口。

咱們如何理解DIP?

  1. 知道依賴倒置的由來
  • 因爲過去傳統軟件開發方法傾向於高層依賴於低層
  • 現在依賴倒置經過接口隔離,高層和底層都依賴於接口後
  • 結論:從結構上相對於傳統編程方式而言就是倒置了。
  1. 依賴倒置反面教材

結構以下:學習

沒有遵循依賴倒置

代碼以下:this

/** * 高層 */
class GaoCeng {
   ZhongCeng mZhongCeng;
   public GaoCeng(ZhongCeng mZhongCeng) {
       this.mZhongCeng = mZhongCeng;
   }
}
/** * 中層 */
class ZhongCeng{
   DiCeng mDiCeng;
   public ZhongCeng(DiCeng mDiCeng){
       this.mDiCeng = mDiCeng;
   }
}
/** * 底層 */
class DiCeng{
}```

3. 依賴倒置正面教材

> 結構以下:

![遵循依賴倒置.png](http://upload-images.jianshu.io/upload_images/1552955-33a2d00c5821ea72.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
> 代碼以下:

``` java
/** * 中層接口 */
interface ZhongCengInterface{   
}
/** * 高層接口 */
interface GaoCengInterface{   
}
/** * 高層 */
class GaoCeng {
   GaoCengInterface mGaoCengInterface;
   public GaoCeng(GaoCengInterface mGaoCengInterface) {
       this.mGaoCengInterface = mGaoCengInterface;
   }
}
/** * 中層 */
class ZhongCeng implements GaoCengInterface{
   ZhongCengInterface mZhongCengInterface;
   public ZhongCeng(ZhongCengInterface mZhongCengInterface){
       this.mZhongCengInterface = mZhongCengInterface;
   }
}
/** * 底層 */
class DiCeng implements ZhongCengInterface{
}
複製代碼
  1. 結論
  • 能夠從結構圖上明確看出兩種方式依賴結構是相反的,因此叫依賴倒置
  • 經過這種結構咱們能夠肆意的更改具體的接口實現類,而不會影響高層

遵循DIP有什麼好處?

既然咱們理解了DIP,那麼DIP的好處不言而喻。spa

  1. 經過依賴於接口,隔離了具體實現類
  2. 低一層的變更並不會致使高一層的變更
  3. 提升了代碼的容錯性、擴展性和易於維護

既然有好處,那麼就一定有壞處:代碼的增長,學習成本和代碼思考時間的增長。(不過相對於後期的好處,這點咱們仍是能理解的)設計

例子

其實理解DIP的例子就是一個很好的對比例子。 如今來一個實際一點的例子:超重提價code

  • 需求:編寫一個稱重提價裝置,物體2元/斤(物體重量 <= 100)計算。當物體超過100kg提醒,而後超出部分以10元/斤(物體重量 > 100)計算。

以傳統方式編程對象

/** * 稱重器傳統編程 */
class Scales{
    private double readValue;//獲取到的物體的重量
    private double highestValue;
    private double inPrice;
    private double outPrice;
    public Scales(double highestValue, double inPrice, double outPrice) {
        this.highestValue = highestValue;
        this.inPrice = inPrice;
        this.outPrice = outPrice;
    }

    /** * 當有物體放上去後稱重 */
    public void startScales() {
        //...readValue = ? (這裏獲取稱重器計算的重量)
        showWeigh(readValue);
        double price = 0;
        double diff = readValue - highestValue;
        if (diff > 0) {
            outWeighWarn(diff);
            price += highestValue * inPrice;
            price += diff * outPrice;
        } else {
            price += readValue * inPrice;
        }
        showPrice(price);
    }
    /** * 顯示重量 */
    private void showWeigh(double weigh) {
    }

    /** * 超重提醒 */
    private void outWeighWarn(double outWeigh) {}

    /** * 顯示價格 */
    private void showPrice(double price) {
    }
}
複製代碼

依賴倒置後接口

/** 稱重接口*/
interface Weigh {
    double read();
}

/** 最大重量、範圍內價格、範圍外價格的設置*/
interface Value{
    double highestValue();
    double inPrice();
    double outPrice();
}

/** 顯示器接口*/
interface Show {
    void outWeighWarn(double diff);
    void showWeigh(double weigh);
    void showPrice(double price);
}


class Scales{
    private Weigh mWeigh;
    private Show mShow;
    private Value mValue;
    public Scales(Weigh mWeigh, Show mShow, Value mValue) {
        this.mShow = mShow;
        this.mWeigh = mWeigh;
        this.mValue = mValue;
    }

    /** * 當有物體放上去後稱重 */
    public void startScales() {
        mShow.showWeigh(mWeigh.read());
        double price = 0;
        double diff = mWeigh.read() - mValue.highestValue();
        if (diff > 0) {
            mShow.outWeighWarn(diff);
            price += mValue.highestValue() * mValue.inPrice();
            price += diff * mValue.outPrice();
        } else {
            price += mWeigh.read() * mValue.inPrice();
        }
        mShow.showPrice(price);
    }
}
複製代碼

咱們能夠看出依賴倒置後使代碼可複用,能夠是任意的稱重裝置,能夠是任意的顯示裝置,只要它們實現對應的接口便可。高層沒必要在乎底層具體是什麼東西。ip

總結[^foot1]

  • DIP的規則:依賴於抽象,不該該依賴於具體類。
  • 任何變量都不該該持有一個指向具體類的指正或這引用
  • 任何類都不該該從具體類派生
  • 任何方法都不該該覆寫它的任何基類中已經實現了的方法

每一個程序都會有違反這些規則的狀況,有時必須建立具體類的實例。此外,這些規則對於那些具體但卻穩定的類來講彷佛不太合理。若是一個具體類不太會改變,而且也不會建立其餘相似的派生類,那麼依賴於它並不會形成損害,好比說String類型。

  • 然而,咱們編寫的大多數具體類都是不穩定的,咱們將它們隱藏在抽象接口後面,隔離它們的不穩定性。

  • 因爲抽象將高層和細節彼此隔離,因此代碼也很是容易維護

參考文獻

[^foot1]: 敏捷軟件開發 第12章 依賴倒置原則(DIP)

相關文章
相關標籤/搜索