定義:
高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽像不應該依賴細節;細節應該依賴抽象。
問題由來:類A直接依賴類B,假如要將類A改為依賴類C,則必須通過修改類A的代碼來達成。這種場景下,類A一般是高層模塊,負責複雜的業務邏輯;類B和類C是低層模塊,負責基本的原子操作;假如修改類A,會給程序帶來不必要的風險。
解決方案:將類A修改為依賴接口I,類B和類C各自實現接口I,類A通過接口I間接與類B或者類C發生聯繫,則會大大降低修改類A的機率。
依賴倒置原則基於這樣一個事實:相對於細節的多變性,抽象的東西要穩定的多。以抽象為基礎搭建起來的架構比以細節為基礎搭建起來的架構要穩定的多。在java中,抽象指的是接口或者抽像類,細節就是具體的實現類,使用接口或者抽像類的目的是製定好規範和契約,而不去涉及任何具體的操作,把展現細節的任務交給他們的實現類去完成。
依賴倒置原則的核心思想是面向接口編程,我們依舊用一個例子來說明面向接口編程比相對於面向實現編程好在什麼地方。場景是這樣的,母親給孩子講故事,只要給她一本書,她就可以照著書給孩子講故事了。
例子:
違法依賴倒置的情況
public class Student { public void read(Book book){ System.out.println("學生開始閱讀:"+book.getName()); } } public class Book { public String getName() { return "書籍"; } }
當學生需要閱讀網頁時,就需要修改Student類,這是很不友好的設計。下面我們看遵守依賴倒置原則的例子。
public interface Person { public void read(Reader reader); } public interface Reader { public String getName(); } public class Student implements Person{ @Override public void read(Reader reader) { System.out.println("學生開始閱讀:"+reader.getName()); } } public class Book implements Reader { public String getName() { return "書籍"; } } public class Website implements Reader { public String getName() { return "網頁"; } } public class Test { public static void main(String[] args) { Person student = new Student(); student.read(new Book()); student.read(new Website()); } }
在read方法中我們使用了接口作為參數。
總結:
1. 每個類最好有接口或者抽像類,或者同時接口和抽像類都有。
2. 變量聲明最好是接口或者抽像類。
3. 繼承時遵守里氏替換原則。