如果你之前使用过JavaEE开发中的Spring框架的话,那么你一定对依赖注入并不陌生。依赖注入(DI: Dependency Injection)是控制反转(IoC: Inversion of Control)的实现方式之一,另外一种是依赖查找(DL: Dependency Lookup)。当然在Spring框架中主要使用到了控制反转中的依赖注入这种方式。当然在Spring框架中除了依赖注入外,还有一个重要的概念那就是面向切面编程(AOP)。
简单的说,依赖注入负责往类中注入依赖对象,而面向切面编程则负责往里添加代码段的。本篇博客我们主要聊的就是依赖注入,关于AOP和依赖查找的内容,如果以后有机会,会在后边的博客进行介绍。
本篇博客不是使用Java语言实现的,而是使用Objective-C来实现的。实现依赖注入的具体方式就是使用反射机制来实现的,本篇博客,我们就使用OC的反射机制来看一下iOS开发中的依赖注入的实现方式。当然在Java的Spring框架中是在XML文件中配置的JavaBean,入乡随俗,本篇博客我们就使用iOS开发中常用的PList文件来存储类似于JavaBean的东西,也就是数本篇博客我们使用PList文件来代替XML文件。废话少说,进入我们的主题。
一、依赖注入的实现机制
依赖注入,听起来特别高大上,其实真正理解其工作原理后,也没什么东西。本篇博客采用的代码实例与之前我们聊“策略模式”的示例一致。关于之前的策略模式的博客,请移步于《“穿越火线”中的“策略模式”(Strategy Pattern)》。当然我们之前的示例是使用的Swift来实现的,本篇博客使用的是OC, 虽然语言不通,但是思想是一致的。在“策略模式”中我们通过策略模式为不通的军官提供不同的武器策略。而本篇博客,我们依然采用这个思路,不过我们是依照“依赖注入”的方式来为不同的军官提供不同的武器策略。
下方就是我们本篇博客使用示例的类图。WeaponType是所有武器的父类,在其中扮演者“武器接口”的角色。所有的武器都继承自WeaponType。而Character角色类依赖于WeaponType武器接口类,所有Character与WeaponType存在依赖关系。
“依赖注入”从字面上看,就是注入依赖。也就是将依赖关系的对象注入到相应的类中。而在上述示例中,Character依赖于WeaponType接口,如果使用“依赖注入”来解决这层依赖关系的话,就是通过反射机制(“Runtime”)动态的将WeaponType的子类的对象注入到Character对应的依赖属性中去。而反射时需要的依赖关系信息,我们就从PList文件中进行读取,当然Java中是从XML中进行配置的,这就是“依赖注入”。
本篇博客我们就来根据上述类图的依赖关系,来完整的实现这个实例。当然在真正实现时,我们用到的主要核心内容是“面向接口编程”、“面向对象的多态性”、“反射机制”、“PList文件的读取与操作”。下方会一一介绍。
二、示例工程的目录结构
接下来我们就来概述一下本篇博客所使用到的实例的工程结构,也就是先整体的了解一下本篇博客所涉及的示例工程。下方这个工程目录结构就是我们本篇博客所涉及的示例的目录结构。Weapon文件夹中存放的就是武器策略所涉及的武器接口与武器类。Character文件夹中存放的就是武器使用者所对应的目录。PList文件则存储的是Character类依赖WeaponType接口的具体类的依赖信息。Relation类则是负责读取PList文件中的依赖信息,根据这些依赖信息将依赖对象通过“反射机制”注入到相应的类中。
三、PList文件中的内容
本篇博客中的PList文件的作用就类似于Spring框架中用来配置JavaBean的XML。当然我们本篇博客的PList文件的存储内容的结构与形式与Spring中的XML有所不同,但是其作用都是一样的,都是用来描述类之间的依赖关系的。
下方截图就是本篇示例所涉及的PList文件中的内容。从下方文件中,我们可以看出其中存储了三个类的信息,一个是Lieutenant(中尉)类,一个是Captain(上尉)类,最后一个就是Soldier(士兵)类。每个类也就是Java中常说的JavaBean,Relation类可以根据反射机制根据这些类在PList文件中提供的信息来实例化相应的类的对象。
我们就以Lieutenant为例。在PList文件中,其实Lieutenant就对应这一个类,从Lieutenant对应的信息来看,Lieutenant的对象由Character类实例化,但是在实例化时需要给Character的对象依赖属性weapon赋一个HK48Weapon类的对象。当然这一系列实例化以及赋值的动作都是由反射机制完成。稍后我们会给出具体实现。
四、通过PList文件创建类
Relation类就是赋值加载相应的PList文件内容,然后根据其加载的内容利用反射机制生成相应的类的对象。下方代码片段就是Relation类加载上述的PList文件内容,让后给据这些内容生成相应的类的对象的。下方这个代码片段根据Relation提供的上下文分别创建了Lieutenant对象、Captain对象以及Soldier的对象。具体如下所示。
根据上下文创建完对象后,都会调用fire方法,因为每个对象对应的上下文不同,也就是注入的依赖对象不同,所以fire方法执行的结果也不同。下方是上述代码的运行结果,如下所示:
五、使用反射机制注入依赖对象
接下来我们就要来看一下如何使用反射机制来注入依赖对象的,也就是Relation类的具体实现。
1.通过初始化方法提供plist文件
下方是Relation类的构造器,构造器中有个参数plistFileName, 该参数就是用来存储依赖上下文信息的plist文件。Relation类在实例化对象时,收到该文件后,会加载该文件中的上下文信息,也就是我们plist文件中的内容。具体代码如下所示。
下方代码片段就是本篇博客的核心代码,根据PList文件中提供的上下文信息,生成相应的对象,并给对象相应的属性注入依赖对象。当然下方是通过Setter方法来设置依赖对象的。设置完毕后,返回该注入好依赖的对象。具体如下所示。
今天博客就先到这儿吧,以后我们找个合适的时间再聊聊依赖查找和面向切面编程的东西。下方的github链接是本篇博客所涉及Demo的源码分享链接,如下所示。
源码的github分享链接:https://github.com/lizelu/DependencyInjectionForiOS