依賴倒置原則

我的博客原文:
依賴倒置原則java

雪

設計模式六大原則之三:依賴倒置原則。git

簡介

姓名 :依賴倒置原則github

英文名 :Dependence Inversion Principle編程

價值觀 :大男子主義的典型表明,什麼都得經過老大或者老爸贊成設計模式

伴侶 :必定是個溫柔體貼的女子ide

我的介紹工具

  1. High level modules should not depend upon low level modules.Both should depend upon abstractions. 高層模塊不該該依賴低層模塊,二者都應該依賴其抽象(模塊間的依賴經過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是經過接口或抽象類產生的)
  2. Abstractions should not depend upon details. 抽象不該該依賴細節(接口或抽象類不依賴於實現類)
  3. Details should depend upon abstractions. 細節應該依賴抽象(實現類依賴接口或抽象類)

給你們講個故事,我胡亂想的,若有雷同,確定是英雄所見略同。那必須交個朋友。測試

一個小村裏,有兩家飯館,雖然掛着不一樣的牌子,挨在一塊兒,可是老闆確是表兄弟。這兩兄弟摳得很,爲了節省成本,密謀了一個想法:在兩家飯館誰家忙的時候,可讓不忙的那家的員工過去支援一下。這樣子,原本每家飯館都須要 2 個洗碗工,總共須要 4 個,他們就只招了 3 個,省了 1 個洗碗工的成本,固然不止洗碗工,還有服務員等等。兩兄弟約定了規則:this

  1. A 飯館須要支援的時候,B 飯館老闆,讓 B 飯館老闆選哪一個員工去支援,不能直接讓 A 飯館的員工直接找 B 飯館的員工去幫忙,但可讓 A 飯館員工找 B飯館老闆告知須要支援。
  2. 雖然老闆權利大,可是也不能說 A 飯館老闆直接叫 B 飯館的員工去幫忙。
  3. 員工沒有真實的老闆,今天爲 A 飯館工做就是 A 飯館的員工,沒有跟定哪一個老闆。

大概經過這個小故事,描述了依賴倒置原則的基本內容。設計

代碼復原

下面經過代碼來模擬這個故事。

錯誤的示範

這個錯誤的示範將就看哈,可能有些問題沒描述清楚。

老闆和員工抽象

abstract class Boss {

    abstract void support();

    abstract void askHelp(Boss boss);
}

abstract class Staff {

    private String name;

    abstract void service();

    abstract void askHelp(Boss boss);

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

老闆具體類

class BossA extends Boss {

    private StaffA staffA;

    public BossA(StaffA staffA) {
        this.staffA = staffA;
    }

    @Override
    void support() {
        staffA.service();
    }

    @Override
    void askHelp(Boss boss) {
        boss.support();
    }

}

class BossB extends Boss {

    private StaffB staffB;

    public BossB(StaffB staffB) {
        this.staffB = staffB;
    }

    @Override
    void support() {
        staffB.service();
    }

    @Override
    void askHelp(Boss boss) {
        boss.support();
    }
}

員工具體類

class StaffA extends Staff {

    public StaffA(String name) {
        this.setName(name);
    }

    @Override
    void service() {
        System.out.println(this.getName() + "提供服務");
    }

    @Override
    void askHelp(Boss boss) {
        boss.support();
    }
}

class StaffB extends Staff {

    public StaffB(String name) {
        this.setName(name);
    }

    @Override
    void service() {
        System.out.println(this.getName() + "提供服務");
    }

    @Override
    void askHelp(Boss boss) {
        boss.support();
    }
}

測試代碼

/** 初始化老闆和員工 */
StaffA staffA = new StaffA("A 員工");
StaffB staffB = new StaffB(" B 員工");
Boss bossA = new BossA(staffA);
Boss bossB = new BossB(staffB);

/** A 老闆向 B 老闆求支援 */
bossA.askHelp(bossB); // 打印出:B 員工提供服務

/** B 員工向 A 老闆求支援 */
staffB.askHelp(bossA); // 打印出:A 員工提供服務

好像看起來實現了要求了,可是其實這段代碼沒有按照上面的 3 點規則編寫,破壞了第 3 點規則,老闆們的員工沒有用員工的抽象類,破壞了細節依賴抽象這一點。設想一下,假如如今 A 老闆把 A 員工辭退了,從新招了個 C 員工,那麼怎麼實現呢?是否是須要再新增一個 StaffC 類,而後再修改 BossA 類代碼,把 StaffA 換成 StaffC。這樣超級麻煩,在平時寫項目中要時刻考慮這一點:在具體實現類使用其餘類,是否是能夠用其抽象類?

代碼:

DIPErrorTest.java

正確的示範

看了上面那個憋屈的代碼,再來看下面簡潔的代碼,纔會發現依賴倒置原則是多麼強大。

老闆和員工抽象類

abstract class Boss2 {

    private Staff2 staff;

    public Boss2(Staff2 staff) {
        this.staff = staff;
    }

    abstract void support();

    abstract void askHelp(Boss2 boss);

    public void setStaff(Staff2 staff) {
        this.staff = staff;
    }

    public Staff2 getStaff() {
        return staff;
    }
}

abstract class Staff2 {

    private String name;

    abstract void service();

    abstract void askHelp(Boss2 boss);

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

老闆類

class BossImpl extends Boss2 {

    public BossImpl(Staff2 staff) {
        super(staff);
    }

    @Override
    void support() {
        this.getStaff().service();
    }

    @Override
    void askHelp(Boss2 boss) {
        boss.support();
    }
}

員工類

class StaffImpl extends Staff2{

    public StaffImpl(String name) {
        this.setName(name);
    }

    @Override
    void service() {
        System.out.println(this.getName() + "提供服務");
    }

    @Override
    void askHelp(Boss2 boss) {
        boss.support();
    }
}

測試類

/** 正確示範 */
Staff2 staffA2 = new StaffImpl("A 員工");
Staff2 staffB2 = new StaffImpl("B 員工");
Boss2 bossA2 = new BossImpl(staffA2);
Boss2 bossB2 = new BossImpl(staffB2);

/** A 老闆向 B 老闆求支援 */
bossA2.askHelp(bossB2); // 打印出:B 員工提供服務

/** B 員工向 A 老闆求支援 */
staffB2.askHelp(bossA2); // 打印出:A 員工提供服務

/** A 老闆辭退了 A 員工,換成了 C 員工 */
Staff2 staffC2 = new StaffImpl("C 員工");
bossA2.setStaff(staffC2);

/** B 員工向 A 老闆求支援 */
staffB2.askHelp(bossA2); // 打印出:C 員工提供服務

這代碼相比上面錯誤的示範,簡潔了不少,實現的功能卻更靈活,這就是依賴倒置原則強大的地方,它能夠將類的耦合性下降,提供靈活的處理。

代碼:

DIPRightTest.java

最佳實踐

  1. 變量的表面類型儘可能是接口或者是抽象類
  2. 任何類都不該該從具體類派生
  3. 儘可能不要覆寫基類的方法
  4. 結合里氏替換原則使用
    (來自《設計模式之禪》)

總結

總的來講,要實現依賴倒置原則,要有『面向接口編程』這個思惟,掌握好這個思惟後,就能夠很好的運用依賴倒置原則。

參考資料:《大話設計模式》、《Java設計模式》、《設計模式之禪》、《研磨設計模式》、《Head First 設計模式》

但願文章對您有所幫助,設計模式系列會持續更新,感興趣的同窗能夠關注公衆號,第一時間獲取文章推送閱讀,也能夠一塊兒交流,交個朋友。

公衆號之設計模式系列文章

公衆號

相關文章
相關標籤/搜索