1. 程式人生 > 實用技巧 >設計模式6大原則之【依賴倒置原則】

設計模式6大原則之【依賴倒置原則】

依賴倒置原則(Dependence Inversion Principle,DIP)

定義

  • 高層模組不應該依賴低層模組,兩者都應該依賴其抽象
  • 抽象不應該依賴細節,細節應該依賴抽象
  • 針對介面進行程式設計而不是針對實現程式設計

那什麼是抽象?什麼又是細節呢?在Java語言中,抽象就是指介面或抽象類,兩者都是不能直接被例項化的;細節就是實現類,實現介面或繼承抽象類而產生的類就是細節,其特點就是可以直接被例項化,也就是可以加上一個關鍵字new產生一個物件。依賴倒置原則在Java語言中的表現就是:
模組間的依賴通過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是通過介面或抽象類產生的;介面或抽象類不依賴於實現類;實現類依賴介面或抽象類。更加精簡的定義就是“面向介面程式設計”——OOD(Object-Oriented Design,面向物件設計)的精髓之一。

優點

可以減少類之間的耦合性、提高系統的穩定性,提高程式碼的可讀性和可維護性,降低修改程式造成的風險。

程式碼案例

有如下程式碼,學生類(Student)中定義了學習語文、英語的方法,測試類(Test)進行呼叫

Student類

public class Student {
    public void StudyChinese(){
        System.out.println("學生學習語文");
    }
    public void StudyEnglish(){
        System.out.println("學生學習英語");
    }
}

Test類

public class Test {
    public static void main(String[] args) {
        Student student = new Student();
        student.StudyChinese();
        student.StudyEnglish();
    }
}

我們執行程式,有如下輸出:

學生學習語文
學生學習英語

新需求

此時我們希望學生可以學習數學、學習物理等課程怎麼辦呢

在Student類中新增方法 StudyMath

public void StudyMath(){
   System.out.println("學生學習數學");
}

這種做法會產生一個問題:低層模組Student類會經常變化,導致程式碼經常修改,這與我們的依賴倒置原則是相違背的

面向抽象

我們將學習課程抽象為一個介面(ICourse)和多個實現類,讓學生類(Student)依賴學習的介面(ICourse)而不是具體的實現,修改後的程式碼如下:
ICourse:

public interface ICourse {
    void study();
}

ChineseCourse:

public class ChineseCourse implements ICourse{
    @Override
    public void study() {
        System.out.println("學生學習語文");
    }
}

EnglishCourse:

public class EnglishCourse implements ICourse {
    @Override
    public void study() {
        System.out.println("學生學習英語");
    }
}

Student:

public class Student {
    public void Study(ICourse course){
        course.study();
    }
}

ICourse:

public class Test {
    public static void main(String[] args) {
        Student student = new Student();
        student.Study(new ChineseCourse());
        student.Study(new EnglishCourse());
    }
}

此時我們執行程式,列印的結果和之前一致,此時我們想要加入數學課程,只需要新建一個數學類(MathCourse),然後再上端進行呼叫就可以

public class MathCourse implements ICourse {
    @Override
    public void study() {
        System.out.println("學生學習數學");
    }
}

呼叫: student.Study(new MathCourse());

這樣就做到了在不修改原有類的基礎上,新增了功能,當然上層類是肯定要做出修改的,這是不可避免的。

依賴倒置的三種寫法

1.介面宣告依賴物件(上述案例中的方式)

public class Student {
    public void Study(ICourse course){
        course.study();
    }
}

2.通過建構函式傳遞依賴物件

public class Student {
    private ICourse course;
    public Student(ICourse course) {
        this.course = course;
    }
    public void Study(){
        this.Study();
    }
}

3.通過Setter依賴注入

public class Student {
    private ICourse course;
    public void setCourse(ICourse course) {
        this.course = course;
    }
    public void Study(){
        course.study();
    }
}

最佳實踐

  • 每個類儘量都有介面或抽象類,或者抽象類和介面兩者都具備(這是依賴倒置的基本要求,介面和抽象類都是屬於抽象的,有了抽象才可能依賴倒置)
  • 變數的表面型別儘量是介面或者是抽象類
  • 任何類都不應該從具體類派生
  • 儘量不要覆寫基類的方法:如果基類是一個抽象類,而且這個方法已經實現了,子類儘量不要覆寫。類間依賴的是抽象,覆寫了抽象方法,對依賴的穩定性會產生一定的影響