什么是依賴倒轉原則
依賴倒轉(Dependence Inversion Principle ):是程序要依賴於抽象接口,不要依賴於具體實現。簡單的說就是要求對抽象進行編程,不要對實現進行編程,這樣就降低了客戶與實現模塊間的耦合。
1.抽象不應該依賴於細節,細節應該依賴於抽象。
2.高層模塊不依賴底層模塊,兩者都依賴抽象。
我們舉個例子:電腦有不同的組件,硬盤,內存,主板。
硬盤抽象類
1 //硬盤抽象類 2 public abstract class HardDisk { 3 public abstract void doSomething(); 4 }
具體硬盤(希捷硬盤)
1 //希捷硬盤 2 public class XiJieHardDisk extends HardDisk { 3 4 public void doSomething() { 5 System.out.println("希捷硬盤"); 6 } 7 8 }
具體硬盤(西數硬盤)
1 public class XiShuHardDisk extends HardDisk { 2 3 public void doSomething() { 4 System.out.println("西數硬盤"); 5 } 6 7 }
主板抽象類
1 //主板抽象類 2 public abstract class MainBoard { 3 public abstract void doSomething(); 4 }
具體主板(華碩主板)
1 public class HuaShuoMainBoard extends MainBoard{ 2 3 public void doSomething() { 4 System.out.println("華碩主板"); 5 } 6 7 }
具體主板(微星主板)
1 public class WeiXingMainBoard extends MainBoard { 2 3 public void doSomething() { 4 System.out.println("微星主板"); 5 } 6 7 }
內存抽象類
1 //內存抽象類 2 public abstract class Memory { 3 public abstract void doSomething(); 4 }
具體內存(金士頓內存)
1 public class JinShiDunMemory extends Memory { 2 3 public void doSomething() { 4 System.out.println("金士頓內存"); 5 } 6 7 }
具體內存(三星內存)
1 public class SanxingMemory extends Memory { 2 3 public void doSomething() { 4 System.out.println("三星內存"); 5 } 6 7 }
現在,電腦的各個零部件都有了,只差電腦了。首先,我們不按照依賴倒轉原則,按照傳統模式
傳統的過程式設計傾向於使高層次的模塊依賴於低層次的模塊,抽象層依賴於具體的層次。
這樣,電腦應該是這樣的
1 //電腦 2 public class Computer{ 3 private HuaShuoMainBoard huaShuoMainBoard; 4 private JinShiDunMemory jinShiDunMemory; 5 private XiJieHardDisk xiJieHardDisk; 6 7 public HuaShuoMainBoard getHuaShuoMainBoard() { 8 return huaShuoMainBoard; 9 } 10 public void setHuaShuoMainBoard(HuaShuoMainBoard huaShuoMainBoard) { 11 this.huaShuoMainBoard = huaShuoMainBoard; 12 } 13 public JinShiDunMemory getJinShiDunMemory() { 14 return jinShiDunMemory; 15 } 16 public void setJinShiDunMemory(JinShiDunMemory jinShiDunMemory) { 17 this.jinShiDunMemory = jinShiDunMemory; 18 } 19 public XiJieHardDisk getXiJieHardDisk() { 20 return xiJieHardDisk; 21 } 22 public void setXiJieHardDisk(XiJieHardDisk xiJieHardDisk) { 23 this.xiJieHardDisk = xiJieHardDisk; 24 } 25 }
這時,要組裝一台電腦
public class MainClass { public static void main(String[] args) { Computer computer = new Computer(); computer.setHuaShuoMainBoard(new HuaSuoMainBoard()); computer.setJinShiDunMemory(new JinShiDunMemory()); computer.setXiJieHardDisk(new XiJieHardDisk()); computer.setHuaShuoMainBoard(new WeiXingMainBoard());//報錯,無法安裝 } }
可以看到,這種情況下,這台電腦就只能安裝華碩主板,金士頓內存和希捷硬盤了,這對用戶肯定是不友好的,用戶有了機箱肯定是想按照自己的喜好,選擇自己喜歡的配件。
電腦就是高層業務邏輯,主板,內存,硬盤就是中層模塊,還有更低的底層模塊我們沒有寫那么細,但都是一個意思,這樣的方式顯然是不可取的。
下面,我們改造一下,讓Computer依賴接口或抽象類,下面的模塊同樣如此
Computer
1 public class Computer { 2 private MainBoard mainBoard; 3 private Memory memory; 4 private HardDisk harddisk; 5 6 public MainBoard getMainBoard() { 7 return mainBoard; 8 } 9 10 public void setMainBoard(MainBoard mainBoard) { 11 this.mainBoard = mainBoard; 12 } 13 14 public Memory getMemory() { 15 return memory; 16 } 17 18 public void setMemory(Memory memory) { 19 this.memory = memory; 20 } 21 22 public HardDisk getHarddisk() { 23 return harddisk; 24 } 25 26 public void setHarddisk(HardDisk harddisk) { 27 this.harddisk = harddisk; 28 } 29 }
這時,再組裝
1 public class MainClass { 2 public static void main(String[] args) { 3 Computer computer = new Computer(); 4 computer.setMainBoard(new HuaSuoMainBoard()); 5 computer.setMemory(new JinShiDunMemory()); 6 computer.setHarddisk(new XiJieHardDisk()); 7 8 computer.setMainBoard(new WeiXingMainBoard());//完全沒有問題 9 } 10 }
這樣,用戶就可以根據自己的喜好來選擇自己喜歡的品牌,組裝電腦了。
為什么要采取依賴倒轉這種方式
面向過程的開發,上層調用下層,上層依賴於下層,當下層劇烈變動時上層也要跟着變動,這就會導致模塊的復用性降低而且大大提高了開發的成本。
面向對象的開發很好的解決了這個問題,一般情況下抽象的變化概率很小,讓用戶程序依賴於抽象,實現的細節也依賴於抽象。即使實現細節不斷變動,只要抽象不變,客戶程序就不需要變化。這大大降低了客戶程序與實現細節的耦合度。
依賴倒轉模式應用實例
1.工廠方法模式
2.模板方法模式
3.迭代模式
綜上所訴,我們可以看出一個應用中的重要策略決定及業務模型正是在這些高層的模塊中。也正是這些模型包含着應用的特性。但是,當這些模塊依賴於低層模塊時,低層模塊的修改將會直接影響到它們,迫使它們也去改變。這種境況是荒謬的。應該是處於高層的模塊去迫使那些低層的模塊發生改
變。應該是處於高層的模塊優先於低層的模塊。無論如何高層的模塊也不應依賴於低層的模塊。而且,我們想能夠復用的是高層的模塊。通過子程序庫的形式,我們已經可以很好地復用低層的模塊了。當高層的模塊依賴於低層的模塊時,這些高層模塊就很難在不同的環境中復用。但是,當那些高層模塊獨
立於低層模塊時,它們就能很簡單地被復用了。這正是位於框架設計的最核心之處的原則。