一、缘由 public ClassB{
ClassA classA=new ClassA();//classA即将向ClassB提供服务 public executebussiness(){ classA.bussiness();//调用了ClassA的bussiness方法来得到服务 } } 另两个概念就是服务提供者与服务使用者,从上例可以看出,ClassA就是服务提供者-提供业务服务的一方 ,ClassB就服务使用者(也称消费者)-使用业务服务的一方,在他们之间采用的联系的方式我们叫做通信。 从上面的例子可以看出,服务提供者提供服务给消费者是特别直接的。这样有如下坏处: 如果服务提供者变了呢?这样是不是需要改动每个服务消费者? 这样的一种依赖关系?我们用什么来解决呢?Ioc,控制反转,它让服务消费者不直接依赖于服务提供者! 三、什么是控制反转(Ioc)? 一种让服务消费者不直接依赖于服务提供者一 种组件设计方式,一种减少类与类之间依赖的设计原则,一种使服务与服务提供者完全分开的设计原则。我们将第一个例子改进一下: public Class MyClass{ Logger _logger; public void setLogger(Logger logger){ this._logger=logger; } public void helloWorld(){ _logger.DEBUG("Start DEBUG print...."); System.out.println(“hello world"); _logger.DEBUG("End DEBUG print....."); } } 现在调用时: public Class UseLogger{ Logger _logger=Logger.getLogger(this.getClass()); public void execute(){ MyClass myClass=new MyClass(); myClass.setLogger(_logger); myClass.helloWorld(); } } 这样,我们发现,整个使用Logger的控制权全部在客户端,即Logger给MyClass提供的服务的方式已经提交给了最终客房,完全取决于客户端的需要,而不是在MyClass调用服务时就依赖于Logger了,这样,服务提供者(Logger)与消费者(MyClass)之间的依赖就少了很多。这就是Ioc的一种具体实现方式。 我们可以看出来,Ioc更通俗的一种解释就是:我在需要服务时,才告诉你怎么去给我提供服务_logger的定义在MyClass中只是一个预留的定义,直到UseLogger最终消费时才将具体怎么使用Logger通知给MyClass. 四、控制反转的几种设计方式?
目前控制反转的几种实现方式: .基于方法的(Method-based Ioc,Type-0) .基于接口的(Interface-based Ioc,Type-1) .基于设值的(Setter-based Ioc,Type-2) .基于构造的(Construtor-based Ioc,Type-3) .Type-0 基于方法的实现,主要依赖的是接口,,服务提供者将自己的接品传递给消费者方法,而不是将具体的实现类型传递过去,如下: public interface MyClass{ public void helloWorld(Logger _logger); } 调用时: Logger _logger=Logger.getLogger(this.getClass()); MyClass myClass=new MyClassImpl(); myClass.helloWorld(_logger); 从上面可以看出,这样Logger与MyClass的具体服务都依赖于接口MyClass,Logger提供服务时也止于方法上,Logger服务提供与 MyClassImpl消费之间隔开了, .Type-1 基于接口的实现,主要是采用容器来管理服务提供者,消费者只要实现服务提供者的接口就可以达到服务的目的了。 如下: Public Class MyClassImpl implements MyClass,Logger { protected Logger _logger; /* *实现Logger的enableLogger有效方法 */ public void enableLogger(Logger logger){ _logger=logger; } public void helloWorld(){ _logger.DEBUG("ENABLE"); } } 怎么调用 : MyClass myClass=new MyClass(); Logger _logger=Logger.getLogger(this.getClass()); Container.enableLogger(myClass,_logger); myClass.helloWorld(); 可以看出,这样的实现,必须要有珍上容器来检查是否实现了Logger接品,如果实现凶才能有效,否则就没有作用。这样的方法大大的减少了类与类之间的依赖.但必须要实现一个容器。而且发现,每一个实现的类都直接跟服务提供者(本例就是Logger)的接口有了很直接的联系。 .Type-2 的方式,就是通过BEAN的setter方法来给成员变量赋值,我们采用这种方式,就可以通过一个配置文件,配置好相应的Bean,然后通过一个容器来得到这个服务Bean,这样组件之间的依赖就已经少了。 如: public Class MyClass{ private Logger logger; public void setLogger(Logger logger){ this.logger=logger; } public void helloWorld(){ logger.DEBUG("ENABLE"); } } 如Spring采用的方式就是如此,它的全过程如下: 配置文件: 调用: InputStream is=Client.class.getClassLoader().getResourceAsStream("bean.xml"); BeanFactory beans=new XMLBeanFacotory(is); MyClass myClass=(MyClass)beans.getBean("myClass"); myClass.helloWorld(); 从上面可以看出,所有的服务提供类及消费类都已经通过一个容器类来提取配置文件统一管理了。 .Type-3
基于构造的Ioc,顾名思义,就是通过构造子来传递服务提供者的具体实例来实例化服务接口。 如下: public Class MyClass{ private Logger _logger; public MyClass(Logger logger){ _logger=logger; } public void helloWorld(){ _logger.DEBUG("ENABLE"); } } 这样,注册服务组件时在构造方法处完成,但,这样一来继承和构造时都有一定的困难。 五、结语 Ioc的一个真正的目的就是移植,减少组件的依赖性!但我们通过上面的例子,有没有发现,这中间的依赖还很大呢?比如:如果我要将Logger类移植使用另外一个公司的产品的时候,这个时候,要移植的量是不是还很大呢? 六、附录 1、术语 控制反转(Ioc-Inversion Of Control); 容器(container),用于组件创建,生存的环境; 框架(framework):一组用于对特定领域构造的可复用的基础组件 |