对接口,反转控制(IoC)依赖注入(Denpendency Injection)的个人理解以及Spring Beans初探

接口

首先考虑一种应用场景,我需要设计一个程序,这个程序给用户提供根据关键词搜索的功能,于是我决定用两个类来解决这个问题,一个是MovieLister,一个是MovieFinder,MovieLister负责处理用户输入,MovieFinder负责从数据库搜索相关关键词并返回相关电影名等信息给MovieLister。以下是初始的代码

public class MovieFinder {
	public List findAll(){
		……	//相关代码
	}
}

public class MovieLister {
	private MovieFinder finder;
	public Movie[] moviesDirectedBy(String arg) {
		List allMovies = finder.findAll();
		for (Iterator it = allMovies.iterator(); it.hasNext();) {
			Movie movie = (Movie) it.next();
			if (!movie.getDirector().equals(arg)) 
				it.remove(); 
		}
		return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
	}
	public MovieLister() {
		finder = new MovieFinder();
	}
}

正当我洋洋得意的时候发现事情没那么简单,因为MovieFinder需要有多种方式来实现findAll()方法,比如说,某一个MovieFinder需要从数据库找相应电影信息,而另一个需要从某一个txt文件中找,还有一个需要从网络中爬取数据再来找,这个时候如果简单的考虑,就需要用到6个类来解决这个问题,即MovieFinder1,MovieLister1(这两项用来处理数据库的数据),MovieFinder2,MovieLister2(处理txt文件中的数据)……等等,显然这个时候代码的复用率非常低,质量非常差。

可以注意到三个Lister做的事情都是一样的(甚至百分之九十九的代码都一样),只是其中的具体的Finder不一样而已,而且三个Finder对外提供的功能都是一样的,只是内部实现的方式不同而已,这样我们就可以定义一个接口,这个接口是一个完全的抽象类,其中需要定义该接口对外提供的功能,而继承这个接口的子类都需要重载其中定义的抽象函数来实现具体的功能。这时的代码如下

public interface MovieFinder {
	public List findAll();
}

public class MovieFinderDatabase implement MovieFinder{
	@override
	public List findAll(){
		……//具体实现
	} 
}

public class MovieFinderTxt implement MovieFinder{
	@override
	public List findAll(){
		……//具体实现
	} 
}

public class MovieFinderWeb implement MovieFinder{
	@override
	public List findAll(){
		……//具体实现
	} 
}

public class MovieLister {
	private MovieFinder finder;
	public Movie[] moviesDirectedBy(String arg) {
		List allMovies = finder.findAll();
		for (Iterator it = allMovies.iterator(); it.hasNext();) {
			Movie movie = (Movie) it.next();
			if (!movie.getDirector().equals(arg)) 
				it.remove(); 
		}
		return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
	}
	public MovieLister() {
		/*!!!注意此处有改动!!!*/
		finder = new MovieFinderDatabase();	
	}
}

经过改造,代码的复用率极大的提高,当有新的MovieFinder时,只需要让其继承接口即可。

反转控制(Inversion of Control)

对于改造后的上面的例子,我们来分析一下类与类之间的关系
三个类之间的关系
如上图所示,具体的MovieFinderImpl实现接口MovieFinder,MovieLister中有MovieFinder且要创建具体的Finder。
但这中关系存在很大的问题,就是MovieLister与具体的MovieFinderImpl之间不应该存在依赖关系,即MovieLister中不管是任何类型的finder都应该能正常工作,也就是说finder的类型和Lister无关。
在这一例子中,MovieLister中不应该存在任何创建具体的Finder的语句存在,而是由第三方来创建,并将其赋予MovieLister,这一思想便是反转控制。
这一思想的最大的好处便是降低类之间的耦合度,方便由第三方框架来创建,程序员在编程时只需要定义好各个组件与组件之间的依赖关系,第三方框架(可以称之为容器)利用这些信息在程序运行时将各个组件连在一起。而这一工作可以由依赖注入的方式来实现。

依赖注入

还是上述例子,经过反转控制改造后类之间的关系如下图

该应用中的各个组件都由assembler来创建,并且当MovieLister需要finder时,由assembler来创建具体的finder并将该finder注入MovieLister,具体有三种注入方式

  • 构造函数注入
  • setter(不知道该怎么翻译了)函数注入
  • 接口注入

在介绍注入方式之前,先了解一下图中几个部件的具体意义。显然,MovieLister,MovieFinder,MovieFinderImpl都是由程序员定义的类,而图中的Assembler也叫做容器,是属于第三方的框架,作为第三方框架,它需要程序员告诉它各个组件以及之间的关系,所以一套框架需要
1.组件遵循某种规定
2.被告知(通过代码或配置文件)哪一组件与哪一接口有关系。

本文以Spring框架为例说明。
Spring中的依赖注入方式:setter函数注入

class MovieLister...
public void setFinder(MovieFinder finder) {
	this.finder = finder;
}


class MovieFinderTxt...
public void setFilename(String filename) {
	this.filename = filename;
}

每一个需要注入的类中都需要在其中设置相应属性对用的set函数,并且在相应的配置文件中配置其对应关系如下

<beans>
	<bean id="MovieLister" class="spring.MovieLister">
		<property name="finder">
			<ref local="MovieFinder"/>
		</property>
	</bean>
	<bean id="MovieFinder" class="spring.MovieFinderTxt">
		<property name="filename">
			<value>movies1.txt</value>
		</property>
	</bean>
</beans>

使用方法

public void testWithSpring() throws Exception {
	ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml");
	MovieLister lister = (MovieLister) ctx.getBean("MovieLister");
	Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
	assertEquals("Once Upon a Time in the West", movies[0].getTitle());
}

最后附一个关于SpringBeans的Demo的链接:
SpringBeansDemo
(此处挖个坑,可能会出关于Demo的详解)

注:本文所用的例子,图片和代码(稍作了修改)都取自老师的课堂讲义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值