依賴倒置原則
所謂依賴倒置原則(Dependence Inversion Principle )就是要依賴於抽象,不要依賴於具體。簡單的說就是對抽象進行編程,不要對實現進行編程,這樣就降低了客戶與實現模塊間的耦合。
面向過程的開發,上層調用下層,上層依賴於下層,當下層劇烈變化時,上層也要跟着變化,這就會導致模塊的復用性降低而且大大提高了開發的成本。
面向對象的開發很好的解決了這個問題,一般的情況下抽象的變化概率很小,讓用戶程序依賴於抽象,實現的細節也依賴於抽象。即使實現細節不斷變化,只要抽象不變,客戶程序就不需要變化。這大大降低了客戶程序域實現細節的耦合度。
比如一個合資汽車公司現在要求開發一個自動駕駛系統,只要汽車上安裝上這個系統,就可以實現無人駕駛,該系統可以在福特車系列和本田車系列上使用。面向過程的結構圖:
實現代碼如下:
public class HondaCar
{
public void Run() { Console.WriteLine("本田車啟動了!"); }
public void Turn() { Console.WriteLine("本田車拐彎了!"); }
public void Stop() { Console.WriteLine("本田車停止了!"); }
}
public class FordCar
{
public void Run() { Console.WriteLine("福特車啟動了!"); }
public void Turn() { Console.WriteLine("福特車拐彎了!"); }
public void Stop() { Console.WriteLine("福特車停止了!"); }
}
public class AutoSystem
{
public enum CarType{ Ford,Fonda}
private HondaCar hondcar=new HondaCar();
private FordCar fordcar=new FordCar();
private CarType type;
public AutoSystem(CarType carType)
{
this.type = carType;
}
public void RunCar()
{
if (this.type == CarType.Fonda)
{
hondcar.Run();
}
else if (this.type == CarType.Ford)
{
fordcar.Run();
}
}
public void StopCar()
{
if (this.type == CarType.Fonda)
{
hondcar.Stop();
}
else if (this.type == CarType.Ford)
{
fordcar.Stop();
}
}
public void TurnCar()
{
if (this.type == CarType.Fonda)
{
hondcar.Turn();
}
else if (this.type == CarType.Ford)
{
fordcar.Turn();
}
}
}
顯然這個實現代碼也可滿足現在的需求。
但是如何現在公司業務規模擴大了,該自動駕駛系統還要把吉普車也兼容了。這些就需要修改AutoSystem類如下:
public class AutoSystem
{
public enum CarType{ Ford,Fonda,Jeep}
private HondaCar hondcar=new HondaCar();
private FordCar fordcar=new FordCar();
private Jeep jeep = new Jeep();
private CarType type;
public AutoSystem(CarType carType)
{
this.type = carType;
}
public void RunCar()
{
if (this.type == CarType.Fonda)
{
hondcar.Run();
}
else if (this.type == CarType.Ford)
{
fordcar.Run();
}
else if (this.type == CarType.Jeep)
{
jeep.Run();
}
}
public void StopCar()
{
if (this.type == CarType.Fonda)
{
hondcar.Stop();
}
else if (this.type == CarType.Ford)
{
fordcar.Stop();
}
else if (this.type == CarType.Jeep)
{
jeep.Stop();
}
}
public void TurnCar()
{
if (this.type == CarType.Fonda)
{
hondcar.Turn();
}
else if (this.type == CarType.Ford)
{
fordcar.Turn();
}
else if (this.type == CarType.Jeep)
{
jeep.Turn();
}
}
}
通過代碼分析得知,上述代碼也確實滿足了需求,但是軟件是不斷變化的,軟件的需求也是變化的,如果將來業務又擴大了,該自動駕駛系統還有能實現通用、三菱、大眾汽車,這樣我們不得不又要修改AutoSystem類了。這樣會導致系統越來越臃腫,越來越大,而且依賴越來越多低層模塊,只有低層模塊變動,AutoSystem類就不得不跟着變動,導致系統設計變得非常脆弱和僵硬。
導致上面所述問題一個原因是,含有高層策略的模塊,如AutoSystem模塊,依賴於它所控制的低層的具體細節的模塊(如FordCar和HondaCar)。如果能使AutoSystem模塊獨立於它所控制的具體細節,而是依賴抽象,那么我們就可以服用它了。這就是面向對象中的“依賴倒置”機制。如下類圖:
實現代碼如下:
public interface ICar
{
void Run();
void Stop();
void Turn();
}
public class HondaCar:ICar
{
public void Run() { Console.WriteLine("本田車啟動了!"); }
public void Turn() { Console.WriteLine("本田車拐彎了!"); }
public void Stop() { Console.WriteLine("本田車停止了!"); }
}
public class FordCar :ICar
{
public void Run() { Console.WriteLine("福特車啟動了!"); }
public void Turn() { Console.WriteLine("福特車拐彎了!"); }
public void Stop() { Console.WriteLine("福特車停止了!"); }
}
public class Jeep:ICar
{
public void Run() { Console.WriteLine("福特車啟動了!"); }
public void Turn() { Console.WriteLine("福特車拐彎了!"); }
public void Stop() { Console.WriteLine("福特車停止了!"); }
}
public class AutoSystem
{
private ICar car;
public AutoSystem(ICar car)
{
this.car = car;
}
public void RunCar()
{
this.car.Run();
}
public void StopCar()
{
this.car.Stop();
}
public void TurnCar()
{
this.car.Turn();
}
}
現在Autosystem系統依賴於ICar這個抽象,而與具體的實現細節HondaCar:和FordCar無關,所以實現細節的變化不會影響AutoSystem.對於實現細節只要實現ICar即可。即實現細節依賴於ICar抽象。
綜上所述:一個應用中的重要策略決定及業務 正是在這些高層的模塊中。也正是這些模塊包含這應用的特性。但是,當這些模塊依賴於低層模塊時,低層模塊的修改比較將直接影響到他們,迫使它們也改變。這種情況是荒謬的。
應該是處於高層的模塊去迫使那些低層的模塊發生改變。處於高層的模塊應優先於低層的模塊。無論如何高層模塊也不應該依賴於低層模塊。而且我們想能夠復用的是高層的模塊,只有高層模塊獨立於低層模塊時,復用才有可能。
總之,高層次的模塊不應該依賴於低層次的模塊,它們都應該依賴於抽象。抽象不應該依賴於具體,具體應該依賴於抽象。