Android常见架构模式-依赖注入模式

我们来设想一下使用Android框架来实现UI界面,应该是什么样的过程。

正常的思路可能是这样的:首先要为Android框架定义一个派生类,如Activity的子类,或者实现一个接口类,如View类定义的OnXxxListener()接口类;其次,创建这些类的对象实例;接着,把对象实例注册到框架中去;最后,由框架根据需要调用这些应用层实现的接口方法。

涉及到应用层的操作共有三个步骤:1、定义类;2、new一个对象实例;3、使用该对象作为参数调用框架中的相关方法。这也是我们编写软件时最为常见的流程,再正常没有,也很容易理解,说实话,已经做的非常棒了,是使用模板方法模式来实现IOC模式的经典方式,解耦了新建子类对象和使用这个对象的框架。如果随着业务需求的变动,我们要改变新的业务逻辑,这三步过程仍然要再走一遍。

这种方式确实达到了应用和框架配合工作的目的,不过,我们仔细想一想,应用层是不需要使用这些对象来实现自己的业务逻辑,显然,如果使用了,那就成了正向调用,就不是IOC了。既然应用层不使用它们,但还要让应用层编写代码来创建对象实例并传给框架,应用层可能要抱怨了:我又用不着这些对象,让我创建它们干吗?也就是说应用层和框架层的IOC模块还存在着不必要的耦合:应用层提供了具体的业务功能,它不使用这些业务功能(指应用层不会去调用这些对象实例中的方法),但还要它去调用框架层的代码(产生了耦合)。

为什么不让框架层负责创建呢?于是DI(依赖注入)模式就是专门解决这种场景的,它是IOC的一种实现方式,目的是通过对象注入的方式让框架得到所需要的对象。这样,应用层不再关注何时以及如何把对象实例传给框架,只要提供相关的类就行了,由框架自己在需要的时候负责创建依赖对象的实例,从而由应用层创建对象实例,变成了由框架层自己创建。总之,概括起来就是:DI模式就是由框架层负责创建应用层所定义的类的对象实例,从应用层视角看,就好像是自己定义的类被自动注入到了框架层。可见,在DI模式下,应用层不再为框架层创建所依赖的对象了,在代码层面上把定义类(应用层)和创建该类的对象实例(框架层)给解耦了。

依赖注入有两种方式的应用:
- 提供反向控制IOC:框架层定义了基类,并抽象依赖于这个基类定义的接口方法进行业务处理,应用层定义了这个基类的派生类,并实现或者override基类的接口方法。框架层使用这个派生类创建了对象实例,并供自己使用它来实现具体的业务逻辑。
- 提供工厂方法来管理、控制对象实例:应用层定义类,自己并不创建它的对象实例,当要使用的时候,就调用框架层提供的类似getInstance(String id)方法来得到一个对象。框架层预先或者在应用层调用getInstance()的时候来创建相应的对象实例,实现了工厂方法的功能,框架层可以延迟创建或者缓冲这些对象实例。

实现DI模式的方法一般就是配置文件+ClassLoader+反射,通常应用层完成了接口类的定义之后,把它按照一定的格式要求,在配置文件中进行设置,比如:类的名称、包名、相关参数、是否单例等。框架在运行的时候,解析该配置文件,得到创建这些类的相关信息,然后在合适的时机使用ClassLoader来加载这些类,接着使用反射机制创建出相应的对象实例。

DI模式是很多优秀框架都在使用的一个架构模式,下面看一下该模式在Android的典型应用场景。

我们知道,在开发Android应用时,需要在Manifest.xml中配置我们实现的Application、Activity、Service、ContentProvider、Broadcast等类的派生子类,可是我们从来没有在代码中new一个Activity或者Service对象,那么这些对象是怎么创建出来的呢?聪明的你一定猜测出来了,它就是通过依赖注入的方式创建的。

Andriod应用程序运行的时候会解析Manifest文件,创建Manifest中定义的那些Activity、Service等对象,在需要的时候调用它们,从而实现了应用层的具体业务;再比如,在View的Layout布局文件中,可以在节点处设置View类,如TextView、ImgeView等类,或者应用层自定义的View派生子类;同时也可以为它们指定由应用层自定义的响应事件的OnListener实现类。Android框架会自动扫描这些配置文件中的节点,并解析它们,得到类的名称和创建对象要用到的参数,然后创建出它们的对象实例,并注入到相关业务逻辑代码中,在需要的时候,就调用它们实现的接口方法。这样我们只是实现这些类就行了,无需在代码中创建相应的实例对象。这是第一种应用场景,框架层自己创建对象实例自己使用。

此外,Android开发工程中,有一个res资源目录,里面除了layout之外,还有其他的文件夹,比如color、drawable、anim等资源目录,可以把它们所包含的文件看作是配置文件。Android框架在运行的时候,会解析它们,并创建出相应的对象实例,比如Color、Drawable等类型的对象实例,并以它们的id作为索引号,保存在R.id包下。如果应用程序要使用这些对象,根本不需要通过new操作来创建它们,而是在Context中调用对应的方法接口,比如getColor()、getDrawable()等获取它们就行了,因为这些对象已经都被Android框架在程序运行时创建好了,并且缓存了起来。这是第二种应用场景,框架层创建对象实例供应用层使用,框架层就是创建对象的工厂。

依赖注入模式的好处就是解耦,能够保持软件模块之间的松耦合,为设计开发带来灵活性。我们开发时只需要定义相关类就行了,框架在需要的时候自己会主动创建。如果业务需求变化了,比如,需要替换一个Activity,修改非常简单,只需要重新编写一个新的Activity类,在Manifest中替换掉旧的Activty即可,除了编写一个类外,没有修改其它地方的任何代码,和其它代码没有任何耦合,方便多了。当然,严格地说只是在代码层面上解耦了,把耦合放到了Manifest中去了。定义一个类,修改一下配置文件,就满足了需求变化,无需修改原来的代码,够灵活够方便吧,这难道不爽吗?

至于依赖注入模式的缺点,首先是效率低,因为需要解析配置文件、动态加载类、使用反射来创建对象,不过毕竟DI模式能够为编程带来方便,牺牲点性能应该还能接受的。其次,在创建对象时不够灵活,不如应用层那样可以按照不同的具体场景创建对象。优秀的框架可以做到更灵活一些,如Spring的BeanFactory,它为DI模式定义了许多创建模式,如单例,工厂方法,set/get等,大家若感兴趣可以研究一下。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页