IoC详解
1.定义:IoC是一种模式。IoC(Inversion of Control)中文译为控制反转(即“不用你找,我来提供给你”)。控制反转意味着在系统开发过程中,设计的类将交由容器(spring中是通过applicationContext.xml控制的)去控制,而不是在类的内部去控制,类与类之间的关系将交由容器处理,一个类在需要调用另一个类时,只要调用另一个类在容器中注册的名字就可以得到这个类的实例
1.定义:IoC是一种模式。IoC(Inversion of Control)中文译为控制反转(即“不用你找,我来提供给你”)。控制反转意味着在系统开发过程中,设计的类将交由容器(spring中是通过applicationContext.xml控制的)去控制,而不是在类的内部去控制,类与类之间的关系将交由容器处理,一个类在需要调用另一个类时,只要调用另一个类在容器中注册的名字就可以得到这个类的实例,与传统的编程方式有了很大的不同。Martin Fowler在他的一篇文章中给IoC起了一个更为直观的名字:依赖注射DI(Dependency Injection)。
2.引入IoC:在设计模式中,我们已经习惯一种思维编程方式:Interface Driven Design 接口驱动,接口驱动有很多好处,可以提供不同灵活的子类实现,增加代码稳定和健壮性等等,但是接口一定是需要实现的,也就是如下语句迟早要执行:InterfaceA a = new InterfaceAImp(); InterfaceAImp是接口InterfaceA的一个子类,IoC模式可以延缓接口的实现,根据需要实现,有个比喻:接口如同空的模型套,在必要时,需要向模型套注射石膏,这样才能成为一个模型实体,因此,我们将人为控制接口的实现成为注射(而工厂模式DAOFactory实际是返回的new InterfaceAImp(),即接口的实现类,所以工厂模式可理解为统一注入所有石膏,然后再调用)。
IoC模式是解决调用者和被调用者之间的一种关系,上述InterfaceA实现语句表明当前是在调用被调用者(即InterfaceAImp),由于被调用者名称写入了调用者的代码中,这产生了一个接口实现的原罪:彼此联系,调用者和被调用者有紧密联系,在UML中是用依赖(Dependency)表示。但是这种依赖在分离关注的思维下是不可忍耐的,必须切割,实现调用者和被调用者解耦,新的Ioc模式依赖注射(Dependency Injection)模式由此产生了,也就是将依赖先剥离,然后在适当时候再注射进入。
在Spring中,applicationContext.xml实际就是一个大工厂(这个大工厂的出现,免去了我们针对多个接口,需要定义多个工厂的烦恼);此外,与传统工厂不同,applicationContext.xml是基于IoC的工厂,是更为解耦合的工厂。
3.举例(网上选录)
1)就好比一个皇帝和太监。有一天皇帝想幸某个美女,于是跟太监说,今夜我要宠幸美女。皇帝往往不会告诉太监,今晚几点会回宫,会回哪张龙床,他只会告诉太监他要哪位美女,其它一切都交由太监去安排,到了晚上皇帝回宫时,自然会有美女出现在皇帝的龙床上,这就是控制反转,而把美女送到皇帝的寝宫里面去就是注射,太监就是是框架里面的注射控制器类BeanFactory(在Spring中, 太监相当于applicationContext.xml),负责找到美女并送到龙床上去,整个后宫可以看成是Spring框架,美女就是Spring控制下的接口实现(eg: JavaBean)。
而传统的模式就是一个饥渴男去找小姐出台,找领班,帮助给介绍一个云云,于是领班就开始给他张罗介绍一个合适的给他,完事后,再把小姐还给领班,下次再来。这个过程中,领班就是查询上下文Context(也就是一个负责提供小姐的工厂),领班的一个职能就是给客户找到他们所要的小姐,这就是lookup()方法,领班手中的小姐名录就是工厂类中的各个接口子类,也可以说是JNDI(Java Naming and Directory Interface)。小姐就是接口子类的实现Impl,饥渴男是客户端,青楼是Web容器。
看到区别了么?饥渴男去找小姐出台很麻烦,不仅得找,用完后还得把小姐给还回去,而皇帝爽翻了,什么都不用管,交给太监去处理,控制权转移到太监手中去了,而不是皇帝,必要时候由太监给注射进去就可以了。这就是Spring的美妙中的IoC的美妙之处。
2)设计Girl类和Boy类,其中Girl有kiss()方法,即Girl想要kiss()一个Boy。那么,我们的问题是,Girl如何能够认识这个Boy?在我们中国,常见的MM与GG的认识方式有以下几种:(1)青梅竹马;(2)亲友介绍;(3)父母包办。那么哪一种才是最好呢?
- public class Girl { //青梅竹马:Girl从小就知道自己的Boy
- public void kiss() {
- Boy boy = new Boy();
- boy.kissedByGirl();
- }
- }
- //然而从开始就创建的Boy缺点就是无法在更换。并且要负责Boy的整个生命周期。如果我们的Girl想要换一个怎么办?(严重不支持Girl经常更换Boy-_-~)
- public class Girl { //亲友介绍:由中间人负责提供Boy来见面
- public void kiss(){
- Boy boy = BoyFactory.createBoyById(int id);
- boy.kissedByGirl();
- }
- }
- //实现了工厂(即亲友),这样就将Boy的创建和使用分离开来。如果不满意,尽管另外换一个好了(自己喜欢什么id的Boy,就按照这个id找一个出来)。但是,我们还要处理好与亲友(BoyFactory)的关系,实在是太繁琐了(我为什么一定要这个亲友掺和进来呢?为什么一定要付给她介绍费呢);而且亲友(BoyFactory)经常是以Singleton的形式或者static(Globals)形式出现,这样这个亲友就可以到处去调用createBoyById(int id)方法(万一其她girls爱上了我的男朋友呢?)
- public class Girl { //父母包办:一切交给父母,自己不用费吹灰之力,只需要等着Kiss就好了
- public void kiss(Boy boy){
- boy.kissedByGirl();
- }
- }
- //Well,这是对Girl最好的方法,只要想办法贿赂父母,把Boy交给她。那么就可以轻松的Kiss Boy了。看来几千年传统的父母之命还真是有用哦。
这就是IOC,将对象的创建(青梅竹马模式)和获取(亲友介绍, 即工厂模式)提取到外部。由外部容器提供需要的组件。好莱坞有句名言:"Do not call us, we will call you.", 意思就是:"You, girl, do not call the boy. We will feed you a boy."
面向接口编程(也可以理解为面向抽象编程)是面向对象的核心。一般来说,组件应该分为两部分,即Service(所提供功能的声明)和ServiceImpl(Service的实现)。面向接口好处是:多种实现可以任意切换,防止"everything depends on everything"问题(即具体依赖于具体)。所以,我们的Boy应该是实现Kissable接口。这样一旦Girl不想kiss可恶的Boy的话,还可以kiss可爱的kitty和慈祥的grandmother。下面更进一步看各种框架下Girl得到Boy的不同方式:
- public class Girl {
- private Kissable kissable;
- public Girl() {
- kissable = new Boy();
- }
- public void kissYourKissable() {
- kissable.kissedByGirl();
- }
- }
- //未用IoC模式,Girl自己建立自己的Boy,很难更换,很难共享给别人,只能单独使用,并负责完全的生命周期。
- public class Girl implements Servicable {
- private Kissable kissable;
- public void service(ServiceManager mgr) {
- kissable = (Kissable) mgr.lookup("kissable");
- }
- public void kissYourKissable() {
- kissable.kissedByGirl();
- }
- }
- //Avalon Framework框架(过期)的IoC实现方式:实现Servicable接口就必须实现service()方法,并传入一个ServiceManager。其中会含有需要的其它组件。只需要在service()方法中初始化需要的Boy。
- public class Girl {
- private Kissable kissable;
- public void setKissable(Kissable kissable) {
- this.kissable = kissable;
- }
- public void kissYourKissable() {
- kissable.kissedByGirl();
- }
- }
- //Spring Framework框架的IoC实现方式:通过JavaBean的set方法来将需要的Boy传递给Girl。它必须依赖于配置文件。
- public class Girl {
- private Kissable kissable;
- public Girl(Kissable kissable) {
- this.kissable = kissable;
- }
- public void kissYourKissable() {
- kissable.kissedByGirl();
- }
- }
- //PicoContainer组件的IoC实现方式:通过构造函数传递Boy给Girl。
更多推荐
所有评论(0)