依赖倒置原则
又是一个不可描述的夜晚,酣然入睡,再次醒来已经是来到了一家全球连锁的互联网公司参加面试。一番男默女泪的自我介绍之后,面试官问我什么是ioc,呵!全球连锁的互联网公司居然会问我level如此之低的问题,随即章口就来,IOC就是控制反转( Inversion of Control ),将创建对象与对象生命周期的维护交给Spring的IOC容器管理,将对象的创建化主动为被动,从此在开发过程中不再需要关注对象的创建和生命周期的管理,而是在 需要时由Spring框架提供,这个由spring框架管理对象创建和生命周期的机制称之为控制反转。面试官面无表情地回复了一句就这些?然后呢?然后…然后我就醒了呗。还好是一场梦,面对如此大型的面试,我居然给了如此没有竞争力的回答,该死该死,一夜无眠。 了解IOC之前,先来介绍软件开发中一个重要的思想–依赖倒置原则,先来看一下依赖倒置原则百度给出的定义:**高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。**哦~~ 好有深度呦~~ 依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
依赖倒置原则的核心思想是面向接口编程,我们依旧用一个例子来说明面向接口编程比相对于面向实现编程好在什么地方。场景是这样的,我正在某公司打盹,这时候老板过来拍了拍我的肩膀,又看了一眼手上的劳力士,一脸枯燥,当我以为他要开除我的时候,老板张口了,小姜啊咱们公司的mysql太捞了奥,给我换成oracle。我看了看公司的代码。
代码语言:javascript复制class MysqlDao{
public String getMethod(){
return "公司的数据库是mysql";
}
}
class UserService{
public void userMethod(MysqlDao mysqlDao){
System.out.println(mysqlDao.getMethod());
}
}
public class web{
public static void main(String[] args){
UserService userService = new UserService();
userService.userMethod(new MysqlDao());
}
}
就这点需求,看不起谁呢???干!!!
代码语言:javascript复制class OrcaleDao{
public String getMethod(){
return "公司的数据库换成了orcale";
}
}
class UserService{
public void userMethod(OrcaleDao orcaleDao){
System.out.println(orcaleDao.getMethod());
}
}
public class web{
public static void main(String[] args){
UserService userService = new UserService();
userService.userMethod(new OrcaleDao());
}
}
…虽然简单,但是我好像把三层架构翻了个遍,不行发个朋友圈炫耀一下–今天我居然重构了公司所有的代码…嘻嘻嘻。
老板给我点了个赞之后,伴随着熟悉的BGm他又走来了小姜啊,给你看篇文章OceanBase阿里金服自研数据库
当我不禁在心中呐喊出阿里牛逼的时候,他又掏出了自己的劳力士,对我笑了笑说你懂的。我DNMLGB,老子不干了。老板卒。
如果当初的代码是这样的,我想就不会出现上面的悲剧了吧。
代码语言:javascript复制interface Dao{
public String getMethod();
}
class OrcaleDao implements Dao{
public String getMethod(){
return "公司的数据库换成了orcale";
}
}
class mysqlDao implements Dao{
public String getMethod(){
return "公司的数据库换成了mysql";
}
}
class UserService{
public void userMethod(Dao dao){
System.out.println(dao.getMethod());
}
}
public class web{
public static void main(String[] args){
UserService userService = new UserService();
userService.userMethod(new OrcaleDao());
}
}
我们引入一个持久层的接口,以后所有的数据库都遵从这个接口,这样在进行换数据库的时候,业务层的代码根本就不用我们在关心。这只是一个简单的例子,实际情况中,代表高层模块的Service类将负责完成主要的业务逻辑,一旦需要对它进行修改,引入错误的风险极大。所以遵循依赖倒置原则可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。 传递依赖关系有三种方式,以上的例子中使用的方法是接口传递,另外还有两种传递方式:构造方法传递和setter方法传递,相信用过Spring框架的,对依赖的传递方式一定不会陌生。
在实际编程中,我们一般需要做到如下3点:
- 低层模块尽量都要有抽象类或接口,或者两者都有。
- 变量的声明类型尽量是抽象类或接口。
- 使用继承时遵循里氏替换原则。
依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。
控制反转( Inversion of Control )
依赖倒置原则聊完了,我们再来聊聊今晚的主角IOC,其实IOC就是依赖倒置原则的代码实现,具体采用的方法就是所谓的依赖注入(Dependency Injection)。ioc的思想最核心的地方在于,资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处。第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。也就是说,甲方要达成某种目的不需要直接依赖乙方,它只需要达到的目的告诉第三方机构就可以了,比如甲方需要一双袜子,而乙方它卖一双袜子,它要把袜子卖出去,并不需要自己去直接找到一个卖家来完成袜子的卖出。它也只需要找第三方,告诉别人我要卖一双袜子。这下好了,甲乙双方进行交易活动,都不需要自己直接去找卖家,相当于程序内部开放接口,卖家由第三方作为参数传入。甲乙互相不依赖,而且只有在进行交易活动的时候,甲才和乙产生联系。反之亦然。这样做什么好处么呢,甲乙可以在对方不真实存在的情况下独立存在,而且保证不交易时候无联系,想交易的时候可以很容易的产生联系。甲乙交易活动不需要双方见面,避免了双方的互不信任造成交易失败的问题。因为交易由第三方来负责联系,而且甲乙都认为第三方可靠。那么交易就能很可靠很灵活的产生和进行了。这就是ioc的核心思想。生活中这种例子比比皆是,支付宝在整个淘宝体系里就是庞大的ioc容器,交易双方之外的第三方,提供可靠性可依赖可灵活变更交易方的资源管理中心。另外人事代理也是,雇佣机构和个人之外的第三方。嗯,就这样,晚安好梦。