最近一直在潜心研究ioc。网上找了不少资料,大部分都是英文的。所以突然觉得最近英文水平飞升。这里导入一篇不错的文章,写得不错。相信看懂后,就算是入了ioc的门了。原帖地址:http://ued.sohu.com/article/330

文章标题:反向控制在as中的应用

转帖开始

前面的几句话

先贴一个地址http://www.martinfowler.com/articles/injection.html,此地址链接的文章是专家Martin Fowler撰写的一篇关于反向控制的介绍,已甚为详细,不想看我在下面拾人牙慧咯哩罗嗦的可以直接看此文,然后浏览一下本文关于as反向控制的具体实现即可。

 

何为反向控制?

需要了解几个关键词

反向控制,洋名为Inversion of control,根据Martin Fowler的总结,在应用反向控制时一般是对依赖的反向控制,故此也可称为依赖注入(Dependency Injection)。

所谓依赖注入(Dependency Injection),即对象之间的依赖关系由容器在运行期决定,即由容器动态的将某种依赖关系注入到对象之中。

可以说一个系统起码有两个以上的类来互相合作来完成业务逻辑,通常我们会由每个类各自获取自身所依赖的对象,这样必然会造成代码的耦合性变强。而使用IoC,对象的依赖都是在对象创建时由负责协调系统中各个对象的外部实体提供的,这样使软件组件松散连接成为可能。

Martin Fowler的文章里面提到将依赖注入根据其注入方式的不同可分成三种。

构造函数注入,即在类的构造函数中添加参数来决定其依赖对象。
属性注入,即通过设置类的属性来决定依赖对象。
接口注入,即根据类所实现的接口来决定其依赖对象。 
 说了约等于没说,具体的代码示例可参阅Martin Fowler的文章,这里不赘述了.

使用IoC,需要构建一个抽象的层次,在实现业务逻辑时只依赖抽象,而具体实现则依靠在IoC容器里进行配置,使得程序更为稳健,可维护性更高。不过对于前端项目来说,规模毕竟都不大,IoC的实际使用性仍需评估,这里暂不对适宜性做讨论,只简述一下实施的方法。

As中如何实现反向控制?

要在as中实现反向控制,特别由外部配置文件来完成依赖关系描述的功能,其主要手段还是依靠as的反射机制。

何为反射?

“程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。”

上面的定义是网上抄的,估计也是比较官方的定义。

在as中即表现为通过类名或者类的实例,能够获取到其表示的class对象以及方法,属性等,从而可以动态的创建该类的实例。

如何在as中实现反射?

需注意flash.utils包里面的几个公共函数(简单描述一下,详细说明请参见api)

describeType
此方法为as反射实现的关键所在,此方法的参数可以为一个class对象或者是某class的实例,其返回的结果为一个XML,里面包含该class的类名,超类名,所实现的接口名,方法名,属性名等等。

getDefinitionByName
此方法可以根据类的全名返回当前域与该类名对应的class对象引用。获取到class的引用之后便可以使用new来创建该类的示例了。需要注意需求的class对象是否编译到swf中。

getQualifiedClassName
返回对象的完全限定类名。

getQualifiedSuperclassName
返回 value 参数指定的对象的基类的完全限定类名。

As的反射实现主要是由上面提及的几个方法来实现,而如何实现依赖注入的机制还需考虑IoC容器的构建以及如何进行注入操作。

如何在As实现依赖注入?

用一个简单例子说明。

假设我们需实现的业务为实现一个学生去阅读课本,为了例子需要我们要求有一个学生类 Student,并且有一个阅读的动作类ReadBook,其中有一个read的方法,通过Student对ReadBook的关联调用来实施学生的阅读行为。

一般情况下我们会新建一个Student的实例,在创建Student实例的同时会在其内部创建一个ReadBook的类的实例,然后调用ReadBook的read方法来实现Student的阅读行为。

由于Student依赖了ReadBook的具体实现,这样就造成了Studen与ReadBook的耦合。

使用IoC会如何做呢?

IoC所指的依赖注入应该可以认为在表达类与类的交互时应面对抽象而不应面对其具体实现,这需要我们去建立一个抽象层。
故此本例中首先创建了两个接口。

IreadAction:

 

1 public interface IReadAction 
2     { 
3         function read():void; 
4     }
5

 

Ireader:

 

 

1 public interface IReader 
2     { 
3         function read():void; 
4     }
5

 

 

然后创建一个学生类,该类实现了Ireader接口

 

ios 反向传值方法 ios反向控制_类名

ios 反向传值方法 ios反向控制_依赖注入_02

代码

1 public class Student implements IReader 
 2     { 
 3         public function Student() 
 4         { 
 5         } 
 6         
 7         public var readAction:IReadAction; 
 8   
 9         public function read():void 
10         { 
11             readAction.read(); 
12         } 
13         
14     }
15

 

由于例子需要,创建两个阅读实现类,都实现了IreadAction接口

 

ios 反向传值方法 ios反向控制_类名

ios 反向传值方法 ios反向控制_依赖注入_02

ReadBooks

public class ReadBooks implements IReadAction 
    { 
public function ReadBooks() 
        { 
        } 

public function read():void 
        { 
            trace("read xx books"); 
        } 

    }

 

 

ios 反向传值方法 ios反向控制_类名

ios 反向传值方法 ios反向控制_依赖注入_02

ReadNewspaper

public class ReadNewspaper implements IReadAction 
    { 
public function ReadNewspaper() 
        { 
        } 

public function read():void 
        { 
            trace("read newspaper"); 
        } 

    }

 

 

此处可以明确看到,在Student类实现的时候依赖的是IreadAction而不是其具体实现,而readAction所指向的真正实现类是通过外部容器进行配置。本例中配置文件为外部的一个xml文档。

Config.xml

 

ios 反向传值方法 ios反向控制_类名

ios 反向传值方法 ios反向控制_依赖注入_02

代码

1 <objects> 
 2     <object id="student" clazz="man.Student"> 
 3         <property name="readAction" ref="readBook"/> 
 4     </object> 
 5     <object id="readBook" clazz="man.ReadBooks"> 
 6     </object> 
 7     <object id="readNewspaper" clazz="man.ReadNewspaper"> 
 8     </object> 
 9  </objects>
10

 

在as中应用IoC关键应该是如何去解析注入配置,由于xml有一定的通用性,故此本例采用了xml作为配置文件格式。
在本例中,配置文件中所有ojbect标签所标志的都是会缓存在容器里的对象,其id属性为在容器的中的唯一标识,clazz属性表示其实际类型,
property标签该对象需要配置的属性,标签内的name属性为属性名,ref为对容器里已创建对象引用,其值应该为需引用对象的id标识。
至于如何去解析该配置文件则个人的实现都会有不同,本例解析配置文件的做法只是为实现本例子而简单编写,参考价值不大,可以免阅了。

 

1 //本例关键是这两句,假设从配置文件中获取得到的object类名为className,可简单的如下创建实例 
2  var objectClass:Class = ApplicationDomain.currentDomain.getDefinition(className) as Class; 
3 var objectInstance:* = new objectClass();

 

在本例中的IoC容器有一个getInstanceById方法,即可以通过配置文件中的标识id来获取容器中相应的实例。
于是在解析完配置文件后,实现学生阅读逻辑的时候可以这样:

 

1 Public var student:IReader = 
2 iocContainer.getInstanceById("student"); 
3 student.read();

 

 

有啥好处?

 

试着将

 

<property name="readAction" ref="readBook"/>

 

改为

 

<property name="readAction" ref="readNewspaper"/>

 

于是读报了。

实用不?

老实说这个问题应该结合具体场景来体会,非一言可尽。
不过鄙人感觉如果项目不大,或者说对需求的扩展以及后期可维护可不需多做考虑,而且一个接口就只有一个实现类的话,还要走这样的依赖注入配置,可能就有点被套路套死的感觉。而且前端项目做GUI的事情比较多,如何实在的去应用IoC,这个还需要探讨下。
回顾Martin Fowler提出的三种注入方式,其实有些时候我们在不知不觉中都会使用到IoC,只是可能还没意识到是使用了别人提出过的某种思想而已。
以后会继续探讨一下IoC在前端项目的实际可用性。也会尝试整合一个框架,在实际工作中进行验证,当然这是后话了,有机会再续。
也希望有在实际项目中使用过这种依赖反转的同学可以不吝指教一下,指点一下在前端项目中其优劣之处。
无比感谢。

 

到此转帖结束

以上文章中的例子已经将ioc诠释得淋漓尽致了。现在网上也有很多包含ioc功能的FrameWork。我个人比较推崇的是spring actionscript框架。原因很简单,之前我用pureMVC框架,而spring actionscript将pureMVC扩展了,对于习惯pureMVC的程序员来说,比较亲近。

用上了spring actionscript,就好比拥有一个容器,里面装上了所有需要用到的类,但你需要哪个类的时候,就通过getObject方法获取该类即可。具体的实现日后再说。