从前有一个这样的业务
代码编号01 需求:在MSsqlServer数据库中添加订单信息
- public class MSsqlServer
- {
- public void Insert(){...}
- }
setp 2 构建订单服务,向MSsqlServer环境添加数据
setp 3 整体贯通,实现需求
一个呆萌的员工是这样扩展的
代码编号02 需求变更:改用MysqL存储订单信息
- public class MysqL
- {
- public void Insert(){...}
- }
setp 3 整体贯通,实现需求
一个潜力的员工是这样扩展的
代码编号03 需求变更:改用MysqL存储订单信息
setp 1 构建数据库访问接口
- public interface IDataAccess
- {
- void Insert();
- }
setp 2 实现MysqL环境
- public class MysqLDataAccess : IDataAccess
- {
- public void Insert(){...}
- }
setp 3 改造MSsqlServer环境
- public class MSsqlServerDataAccess : IDataAccess
- {
- public void Insert(){...}
- }
setp 4 改造订单服务,向IDataAccess环境添加数据
- public class OrderService
- {
- private IDataAccess _dataAccess;
-
- public Order(IDataAccess dataAccess)
- {
- _dataAccess = dataAccess;
- }
-
- public void Add()
- {
- _dataAccess .Insert();
- }
- }
setp 5 整体贯通,实现需求
Leader是这样评价的
首先,02和03两段代码都能实现新的需求。
其次,03的代码比02的代码,更加的干净整洁。尤其是在OrderService中。
第三,02的每一次扩展,涉及到每个部分,包括新的实现方式、OrderService和贯通的部分。但03的每一次扩展,OrderService部分不需要改变。新增或是修改,改变了现有的其他模块,就有可能出现Bug,如果能尽可能的不改变一些模块,系统会变的更柔韧。
第四,如果需求要求使用DB2或Oracle来存储订单信息。03代码的实现,毫无疑问的更加容易实现,系统做到了好的扩展性。
第五,如果需求要求使用DB2或Oracle来存储订单信息。03代码的实现,毫无疑问更加简单,也意味着系统更加高效。
依赖倒置设计原则DIP的诞生
总结:03的实现方式带来了种种好处。因为OrderService使用的是存储的接口,不需要关心是使用哪种存储的实现方式。OrderService通过IDataAccess接口来耦合具体的实现,不需要耦合MSsqlServer还是MysqL,由一种强耦合变成了弱耦合,耦合度降低。
如果把最终使用接口的模块OrderService定义为高层模块,把提供服务实现手段的定义为底层模块,总结出:
高层模块需要其他低层模块什么功能,把需求抽象为一个低层模块的接口
各种低层模块的实现按着这个接口去实现
前辈大牛将这句话精炼为
高层模块不应该依赖低层模块,两者应该依赖于抽象 |
抽象不应该依赖于实现,实现应该依赖于抽象 |
这就是依赖倒置设计原则DIP的诞生
生活中的应用
插头标准规定三插头的每个插头的宽度、高度、每两个插头间的距离等标准。插座的设计只要能满足三插头的标准,其他电器的三插头按照标准去实现,这样不管是电视、冰箱还是笔记本的插头,都可以使用这个插座。
反之插头也不需要关心用的插座是公牛还是红牛的。
ATM机的银行卡插槽只要设计满足于银联卡,就不需要关心取款人用的是工行卡还是农行卡。
USB的接口标准统一后,笔记本上的USB插口可以使用鼠标、键盘、U盘、硬盘。