设计模式之依赖倒置原则
What
Dependence Inversion Principle(DIP):高层模块不应该依赖底层模块,都应该依赖于抽象;抽象不应该依赖于具体,具体依赖于抽象
Why
若高层依赖于底层,那么底层的变动也会导致高层的变动,这就会导致模块的复用性降低而且大大提高了开发的成本。若是依赖于抽象的话,那么是比较稳定的,底层或者高层的变动都不会互相影响。
How
很多地方引用的个人觉得也非常有代表性的一个例子:公司是福特和本田公司的金牌合作伙伴,现要求开发一套自动驾驶系统,只要汽车上安装该系统就可以实现无人驾驶,该系统可以在福特和本田车上使用,只要这两个品牌的汽车使用该系统就能实现自动驾驶。
福特汽车类,包含三个方法,启动、停止、转弯
class FordCar { public void Run() { Console.WriteLine("FordCar run"); } public void Stop() { Console.WriteLine("FordCar stop"); } public void Turn() { Console.WriteLine("FordCar turn"); } }
宏达汽车类,包含三个方法,启动、停止、转弯
class HondaCar { public void Run() { Console.WriteLine("HondaCar run"); } public void Stop() { Console.WriteLine("HondaCar stop"); } public void Turn() { Console.WriteLine("HondaCar turn"); } }
自动驾驶系统,有三个方法启动汽车、停止汽车、汽车转向
class AutoSystem { HondaCar hondaCar = new HondaCar(); FordCar fordCar = new FordCar(); CarType type; public AutoSystem(CarType type) { this.type = type; } public void RunCar() { if (type == CarType.Honda) { hondaCar.Run(); } else if (type == CarType.Ford) { fordCar.Run(); } } public void StopCar() { if (type == CarType.Honda) { hondaCar.Stop(); } else if (type == CarType.Ford) { fordCar.Stop(); } } public void TurnCar() { if (type == CarType.Honda) { hondaCar.Turn(); } else if (type == CarType.Ford) { fordCar.Turn(); } } }
目前来看,是满足需求的,但是随着发展业务也在发展,现在如果发展了伙伴,需要对其他品牌的汽车添加自动驾驶系统,比如红旗、奇瑞等品牌,那么如果沿用以前的方式,就需要去修改AutoSystem了,先增加两个新的品牌汽车的对象,然后在启动汽车、停止汽车、汽车转向中进行修改增加分支语句对不同的品牌来进行判断然后加上各种操作,这样就违背的OCP,而且复杂的分支语句也会容易造成错误,如果以后再继续扩展其他的品牌的话,那么这样的程序肯定是不易于维护的,程序的健壮性是较差的,大大的增加了开发的成本。那么敏感的同学已经看出来,既然不同的汽车品牌之间都拥有相同的行为,那么为什么不定义一个接口呢?现在我们先来定义一个接口,然后将各个品牌的汽车实现这个接口实现,那么在AutoSystem中我们就可以针对定义的接口操作了。
interface ICar { void Run(); void Stop(); void Turn(); }
自动驾驶系统也就是高层模块现在针对的是这个抽象的接口,无论什么汽车,只要实现了ICar接口,就能进行相关的操作。
class AutoSystem { ICar car; public AutoSystem(ICar car) { this.car = car; } public void RunCar() { car.Run(); } public void StopCar() { car.Stop(); } public void TurnCar() { car.Turn(); } }
福特汽车类也就是底层模块,实现了ICar接口,现在依赖的是抽象的接口
class FordCar : ICar { public void Run() { Console.WriteLine("FordCar run"); } public void Stop() { Console.WriteLine("FordCar stop"); } public void Turn() { Console.WriteLine("FordCar turn"); } }
宏达汽车类也就是底层模块,实现了ICar接口,现在依赖的是抽象的接口
class HondaCar : ICar { public void Run() { Console.WriteLine("HondaCar run"); } public void Stop() { Console.WriteLine("HondaCar stop"); } public void Turn() { Console.WriteLine("HondaCar turn"); } }
当高层模块依赖底层的时候,那么高层的复用性就较差,就如上例所说的增加汽车品牌种类。如果高层与底层都是依赖于抽象的话,那么高层复用性就较好,因为通过继承象出来的接口实现多态,那么复用的地方就多了,这样的设计无疑是较为稳定的。