參考書籍:設(shè)計模式之禪 --- 秦小波 著
定義:
①高級模塊不應(yīng)該依賴于低級模塊仙蚜,兩者都應(yīng)該依賴于抽象耀找。
②抽象不應(yīng)該依賴于細節(jié)翔悠。
③細節(jié)應(yīng)該依賴于抽象。
那么高級模塊野芒、低級模塊蓄愁,抽象,細節(jié)各指的是什么呢狞悲?
每一個邏輯的實現(xiàn)都是由原子邏輯組成撮抓,不可分割的原子邏輯就是低級模塊。而低級模塊組裝就是高級模塊摇锋。
在Java中丹拯,抽象就是指接口或抽象類,兩者并不能直接被實例化荸恕。
細節(jié)就是實現(xiàn)類乖酬,繼承抽象類或?qū)崿F(xiàn)接口的類就是細節(jié)。特點是可以被直接實例化融求。
那么依賴倒置原則定義在Java語言中可以表示為:
①模塊間的依賴關(guān)系咬像、實現(xiàn)類間的依賴關(guān)系都是通過接口或抽象類產(chǎn)生。
②接口與抽象類不依賴實現(xiàn)類生宛。
③實現(xiàn)類依賴接口或抽象县昂。
從上述定義我們可以看出,依賴倒置原則的核心思想就是:面向接口編程
代碼重現(xiàn):
司機類
public class Driver {
public void drive(Benz benz){
benz.run();
}
}
奔馳類
public class Benz {
public void run(){
System.out.println("開奔馳車上路");
}
}
測試類
public class Demo_01 {
public static void main(String[] args) {
Driver sanmao = new Driver();
sanmao.drive(new Benz());
}
}
--------------------output---------------------------
開奔馳車上路
通過以上代碼完成了司機類開奔馳車的場景茅糜。
但是隨著業(yè)務(wù)需求的變更七芭,現(xiàn)在要求這個司機還能開寶馬車,那么上面代碼必須要重寫(耦合性太強)蔑赘,才能滿足業(yè)務(wù)需求狸驳,顯然上面代碼在設(shè)計上有錯誤预明。
那么我們依據(jù)依賴倒置原則重構(gòu)以上代碼:
司機接口類
public interface IDriver {
void drive(ICar iCar);
}
司機實現(xiàn)類
public class Driver implements IDriver{
@Override
public void drive(ICar iCar) {
iCar.run();
}
}
汽車接口類
public interface ICar {
void run();
}
汽車實現(xiàn)類
public class Benz implements ICar{
@Override
public void run() {
System.out.println("開奔馳上路");
}
}
public class BMW implements ICar {
@Override
public void run() {
System.out.println("開寶馬上路");
}
}
測試類
public class Demo_01 {
public static void main(String[] args) {
IDriver sanmao = new Driver();
ICar benz = new Benz();
ICar BMW = new BMW();
sanmao.drive(benz);
sanmao.drive(BMW);
}
}
--------------------output---------------------------
開奔馳上路
開寶馬上路
Demo_01類就是高級模塊,他對所有低級模塊的依賴都建立在抽象類或接口上
在IDriver接口中傳入ICar接口耙箍,實現(xiàn)模塊間的依賴關(guān)系是通過接口或抽象類產(chǎn)生撰糠。在Driver實現(xiàn)類中也傳入了ICar接口,究竟使用Car的哪個子類還得在測試類中聲明辩昆。
在測試類中阅酪,我們貫徹“②接口與抽象類不依賴實現(xiàn)類≈耄”术辐,所以在測試類Demo_01中,我們都是聲明了各類的抽象施无。
注意:在Java中辉词,只要定義變量就必然要有類型。一個變量可以有兩種類型:表面類型與實際類型猾骡。表面類型是在定義時賦予的類型瑞躺,實際類型是對象的類型,如sanmao的表現(xiàn)類型是IDriver兴想,實際類型為Driver幢哨。
依賴的三種寫法
依賴是可以傳遞的。A對象依賴B對象嫂便,B依賴C捞镰,C依賴D...,
只要做到抽象依賴顽悼,即使是多層的依賴傳遞也無所畏懼曼振。
對象的依賴關(guān)系有以下三種方式傳遞几迄,可以參考Spring的依賴注入
①構(gòu)造函數(shù)傳遞依賴關(guān)系
public interface IDriver {
void drive();
}
public class Driver implements IDriver{
private ICar iCar;
public Driver(ICar _iCar){
this.iCar = _iCar;
}
public void drive() {
this.iCar.run();
}
}
②Setter方法傳遞依賴關(guān)系
public interface IDriver {
void drive();
void setCar(ICar _iCar);
}
public class Driver implements IDriver{
private ICar iCar;
@Override
public void drive() {
this.iCar.run();
}
@Override
public void setCar(ICar _iCar) {
this.iCar = _iCar;
}
}
③接口聲明依賴關(guān)系
public interface IDriver {
void drive(ICar iCar);
}
public class Driver implements IDriver{
@Override
public void drive(ICar iCar) {
iCar.run();
}
}
依賴倒置原則的本質(zhì)就是通過接口或抽象類使得各模塊間相互獨立蔚龙,互不影響,實現(xiàn)松耦合映胁。在使用這個原則時木羹,我們需要注意以下幾個原則:
①每個類都盡可能的都有接口或抽象類,或者抽象類和接口兩者都具備解孙。(依賴倒置原則的基本要求)
②變量的表面類型盡量是接口或者抽象類坑填。(工具類不需要接口或者抽象類,使用類的Clone方法弛姜,必須使用實現(xiàn)類脐瑰,這是JDK的規(guī)范)
③任何類都不應(yīng)該從具體類派生。
④盡量不要覆寫基類的方法廷臼。(若基類是一個抽象類苍在,此方法已經(jīng)實現(xiàn)绝页,那么子類就不應(yīng)該覆寫)
⑤結(jié)合里氏替換原則使用
接口負責public屬性與方法,并且聲明與其他對象的依賴關(guān)系寂恬,抽象類負責公共構(gòu)造部分的實現(xiàn)续誉,實現(xiàn)類準確的實現(xiàn)業(yè)務(wù)邏輯,同時在適當?shù)臅r候?qū)Ω割愡M行細化初肉。
上面講完了依賴關(guān)系與原則酷鸦,那么什么叫倒置呢?
要理解倒置牙咏,首先我們得知道什么叫“正置”臼隔。依賴正置就是類之間的依賴是實現(xiàn)類間的依賴,也就是面向?qū)崿F(xiàn)編程妄壶。但是編寫程序需要的是對現(xiàn)實世界的事物進行抽象躬翁,轉(zhuǎn)化為我們熟知的抽象類或接口,這樣我們就可以將模塊間的依賴關(guān)系盯拱、實現(xiàn)類間的依賴關(guān)系都是通過接口或抽象類產(chǎn)生盒发。這就是我們所說的“倒置”。