設計模式6大原則之【依賴倒置原則】
阿新 • • 發佈:2020-09-10
依賴倒置原則(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();
}
}
最佳實踐
- 每個類儘量都有介面或抽象類,或者抽象類和介面兩者都具備(這是依賴倒置的基本要求,介面和抽象類都是屬於抽象的,有了抽象才可能依賴倒置)
- 變數的表面型別儘量是介面或者是抽象類
- 任何類都不應該從具體類派生
- 儘量不要覆寫基類的方法:如果基類是一個抽象類,而且這個方法已經實現了,子類儘量不要覆寫。類間依賴的是抽象,覆寫了抽象方法,對依賴的穩定性會產生一定的影響