生活中的例子:@H_502_3@
电脑的品牌有很多,但电脑中的所有部件都有标准的接口,不同的厂家都是按照标准去生产各个部件,这些部件的内部实现不同,但接口都是一样的,这样的话,如果我的联想电脑的内存条坏了,我随便买一根内存条插上都能使用。@H_502_3@
之所以我们的电脑能使用任何厂家生产的内存条,是因为这些电脑生产厂家都是“面向接口”生产,而在软件世界中,面向接口编程就是依赖倒转。@H_502_3@
@H_502_3@
依赖倒转的官方定义:@H_502_3@
1.高层模块不应该依赖于低层模块,他们都应该依赖于抽象;@H_502_3@
2.抽象不应该依赖于细节。细节应该依赖于抽象。@H_502_3@
@H_502_3@
@H_502_3@
大白话讲:@H_502_3@
高层模块:调用模块;低层模块:被调用模块。@H_502_3@
高层模块中引用类型的变量不应该指向某一个具体的实现类,而应该指向一个抽象类或接口。@H_502_3@
低层模块应该是一个接口/抽象类的实现类/子类。@H_502_3@
@H_502_3@
面向实现编程的弊处:@H_502_3@
如果是面向实现编程,即高层模块调用低层具有实现的函数,那么当高层模块想要换一种实现的时候,就需要修改高层模块的代码,让它调用另外一个具有实现的函数。这里由于修改了客户端的代码,所以破坏了“封闭修改”的原则。@H_502_3@
@H_502_3@
@H_502_3@
面向接口编程的好处:@H_502_3@
若高层模块一开始调用的是接口中的抽象函数,那么当高层类需要换一个函数实现的时候,高层类就不需要修改任何代码,在低层模块中创建一个实现接口的类,并重写接口中的函数,然后在高层类/低层类中创建实现类的对象并赋给抽象接口的引用即可。@H_502_3@
@H_502_3@
@H_502_3@
高层类想要换一种实现,实现可以在高层类中换,也可以在低层类中换,最好的做法是在配置文件中换。@H_502_3@
1.高层类中换实现@H_502_3@
当低层类创建完一个新的实现类之后,在高层类中需要调用实现类函数的地方作如下修改:@H_502_3@
接口 接口对象 = new 旧的实现类();——>接口 接口对象 = new 新的实现类();@H_502_3@@H_502_3@
2.在低层类中换实现@H_502_3@
低层类中需要再创建一个工厂,接口引用究竟指向哪个实现类的对象就由工厂类决定:@H_502_3@
class Factory(){@H_502_3@
@H_502_3@接口 getBean(){@H_502_3@
@H_502_3@return new 旧的实现类@H_502_3@();@H_502_3@
}@H_502_3@
class Factory(){@H_502_3@
@H_502_3@接口 getBean(){@H_502_3@
@H_502_3@return new 新的实现类@H_502_3@();@H_502_3@
}@H_502_3@
Spring采用了一种更加科学的方式,在配置文件中配置接口对应的实现类是哪一个。这样在程序运行的过程中,首先会读取配置文件,将用户指定的那个实现类的对象赋给接口引用。@H_502_3@