控制反转(Ioc)的设计原则
--减轻组件间的依赖性及藕合性的设计原则
一、缘由
,但面向对象的思想还是复杂,懒惰的人们就想要做更少的工作,因此提出了面向方面!“万事万物皆有姻缘”因为人们的懒惰,所以该放下的就放下了,重新拿起新的事物!那么为什么现在在编程界又提出了控制反转(Ioc)呢?下面我们看一个经常引用的例子:
public Class MyClass{
public void helloWorld(String){
_logger.DEBUG("Start print .....");
System.out.println("Hello! The world!");
_logger.DEBUG("End print.......");
}
}
现在想,如果我实例化_logger的方式变了,变成了:
Logger _logger=Logger.getLogger("My Class DEBUG");
你们想想是不是应该要改上面这个已经实现了的CLASS呢?结果是肯定的,如果我有10个类,100个,那是不是要改10次,100次,我们称这样的软件开发为藕合性太强或依赖太深!
“万事万物皆有姻缘”,控制反转(Ioc)就是为了解决这样的问题而提出的原则!
二、面向对象思想回顾
首先我们回顾面向对象思想的一些内容,笔者在《软件工程之面向对象软件工程基础(软件工程实践之三)》一文中概术面向对象时,面向对象=类+对象+继承+通信,面向对象就是这样的一种思想。将组件分类,使用时再实例化,类与类之间可以继承,他们通过通信联系。在这里我们要重新到一个概念是服务,在很多面向对象文章中都提到了服务,那么什么是服务呢?即一个类向另一个类提供他想要的接口(或方法)。
如下例:
public ClassA{
/*
*下面是ClassA提供给其它类使用的接口
*/
public void bussiness(){
System.out.println("execute bussiness");
}
}
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采用的方式就是如此,它的全过程如下:
配置文件:
<Beans>
<Bean id="logger" Class="Logger"/>
<Bean id="myClass" Class=MyClass">
<property name="logger"><ref bean="logger"/></property>
</Bean>
</Beans>
调用:
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类移植使用另外一个公司的产品的时候,这个时候,要移植的量是不是还很大呢?