依賴倒置原則(Dependence Inversion Principle)
1、依賴倒置原則的定義
1)上層模塊不應該依賴於底層模塊,它們都應該依賴於抽象。
2)抽象不應該依賴於細節,細節應該依賴於抽象,要針對接口編程,不要針對實現編程。
Abstractions should not depend upon details,Details should dependupon abstractions.Program to an interface, not animplementation.
也就是說應當使用接口和抽象類進行變量類型聲明、參數類型聲明、方法返還類型說明,以及數據類型的轉換等。而不要用具體類進行變量的類型聲明、參數類型聲明、方法返還類型說明,以及數據類型的轉換等。要保證做到這一點,一個具體類應當只實現接口和抽象類中聲明過的方法,而不要給出多餘的方法。
基於這個原則,設計類結構的方式應該是從上層模塊到底層模塊遵循這樣的結構:上層類--->抽象層--->底層類。
2、依賴倒置原則與開閉原則的關係
“開-閉”原則與依賴倒轉原則是目標和手段的關係。如果說開閉原則是目標,依賴倒轉原則是到達"開閉"原則的手段。如果要達到最好的"開閉"原則,就要儘量的遵守依賴倒轉原則,依賴倒轉原則是對"抽象化"的最好規範。里氏代換原則是依賴倒轉原則的基礎,依賴倒轉原則是里氏代換原則的重要補充。
3、實例
下面是一個違反了依賴倒轉原則的例子。我們有一個上層類Manager和底層類Worker。我們需要在程序中添加一個新模塊,因爲有新的特殊的工作者被僱用。爲此,我們創建一個新的類SuperWorker。
假設Manager是一個包含非常複雜的邏輯的類,現在爲了引入新的SuperWorker,我們需要修改它。讓我們看一下這有哪些缺點:
(1)我們需要修改Manager類(記住,它是一個非常複雜的類,這需要一些時間和努力)。
(2)Manager類的一些現有功能可能會受到影響。
(3)需要重做單元測試。
所有的這些問題需要大量的時間去解決。但是如果程序的設計符合依賴倒轉原則將會非常簡單。意思是我們設計Manager類和一個IWorker接口以及一些實現了該接口的Worker類。當需要添加SuperWorker類時我們只需要讓它實現IWorker接口。
- //Dependency Inversion Principle - Bad example
- class Worker {
- public void work() {
- // ....working
- }
- }
- class Manager {
- Worker m_worker;
- public void setWorker(Worker w) {
- m_worker=w;
- }
- public void manage() {
- m_worker.work();
- }
- }
- class SuperWorker {
- public void work() {
- //.... working much more
- }
- }
//Dependency Inversion Principle - Bad example class Worker { public void work() { // ....working } } class Manager { Worker m_worker; public void setWorker(Worker w) { m_worker=w; } public void manage() { m_worker.work(); } } class SuperWorker { public void work() { //.... working much more } }
下面是支持依賴倒轉原則的代碼。在這個新的設計中,我們增加了一個新的抽象層IWork接口。現在,上面的問題得到了解決:
不需要修改Manager類。
使對Manager類現有功能的影響最小化。
不需要對Manager類重新進行單元測試。
- //Dependency Inversion Principle - Good example
- interface IWorker {
- public void work();
- }
- class Worker implements IWorker{
- public void work() {
- // ....working
- }
- }
- class SuperWorker implements IWorker{
- public void work() {
- //.... working much more
- }
- }
- class Manager {
- IWorker m_worker;
- public void setWorker(IWorker w) {
- m_worker=w;
- }
- public void manage() {
- m_worker.work();
- }
- }
//Dependency Inversion Principle - Good example interface IWorker { public void work(); } class Worker implements IWorker{ public void work() { // ....working } } class SuperWorker implements IWorker{ public void work() { //.... working much more } } class Manager { IWorker m_worker; public void setWorker(IWorker w) { m_worker=w; } public void manage() { m_worker.work(); } }
4、總結
應用該原則意味着上層類不直接使用底層類,他們使用接口作爲抽象層。這種情況下上層類中創建底層類的對象的代碼不能直接使用new操作符。可以使用一些創建型設計模式,例如工廠方法,抽象工廠和原型模式。
模版設計模式是應用依賴倒轉原則的一個例子。
當然,使用該模式需要額外的努力和更復雜的代碼,不過可以帶來更靈活的設計。不應該隨意使用該原則,如果我們有一個類的功能很有可能在將來不會改變,那麼我們就不需要使用該原則。