依赖倒转以及为何要倒转
高层不应该依赖细节,细节应该依赖高层
SOLID原则里面的D,就是依赖倒转原则。我们为何要依赖倒转?在面向对象中如何利用依赖倒转?
所谓依赖,就是我提到了你。在Java或者Python中我import了你,我提到了你就是对你依赖。当我依赖了你后,如果你发生变化,比如更换了实现,我就不得不也跟着更换,这就产生了耦合。
一个典型的Web调用过程,是从前端页面——>控制器——>业务逻辑——>数据库访问DAO——>数据库。这样一个调用关系。如果没有依赖倒转,那么这个调用关系就是依赖关系,调用方依赖被调用方。但我们的业务逻辑是系统的核心,我们不想他依赖任何东西,最好任何东西都依赖他。
这时候依赖倒转就登场了。而依赖倒转在面向过程的语言中非常难以实现,而在面向对象的语言中非常容易。这个重要特性就是多态。我可以声明一个接口,而这个接口可以有多种实现。通过人工或者框架实现的依赖注入,可以将业务逻辑和数据库等实现完全隔离。比如我一个Service业务,声明如下:
其中QueueRepository、QueueDistributeLock都是接口,在QueueServiceImpl中,需要外部数据库、锁的功能,都通过接口来操作。对QueueServiceImpl来说,他没有依赖任何的数据库实现。
在Spring中,我们用依赖注入的方式来注入实现的QueueRepository和QueueDistributeLock
这个是在另外一个Module里面,和业务逻辑不在同一个Module中。这样,我们的业务逻辑和具体实现完全脱离了。
现在我们的实现是通过数据库和Redis实现的。假如有一天我们要更换实现,由于我们的接口同时具有里氏替换原则,我们只需要在注入的时候给QueueServiceImpl不同的实现,就能实现更换,而不需要修改任何业务逻辑。
同时,由于业务逻辑不依赖任何实现,是单纯的POJO。那么业务逻辑就非常容易测试,外部接口可以用Mock模拟行为。而这正是我们TDD需要测试的部分,因而TDD也变得更加容易。