• 轻量级IOC框架SwiftSuspenders


    该框架的1.6版本位于https://github.com/tschneidereit/SwiftSuspenders/blob/the-past/,现在已经出了重新架构的2.0版本,所以我决定先研究已经成熟的1.6版本,有时间再研究2.0版本的。

    IOC的基本知识

    控制反转(Inversion of Control,英文缩写为IoC)又叫做依赖注入(Dependency Injection,英文缩写为DI),是一种降低耦合度的程序设计模式,它通过将对象的创建过程解耦出来来降低对象间的依赖关系。具体的,在文章依赖注入那些事儿里解释的很详细,另外也可参考老马的文章深度理解依赖注入(Dependence Injection)

    describeType函数介绍

    SwiftSuspenders是利用describeType函数来实现其反射机制的。

    describeType函数可用来生成描述 ActionScript 对象(命名为方法的参数)的 XML 对象。利用此方法可实现 ActionScript 语言的反射编程概念。

    为了详细说明这个函数,我们先自定义一个类MyClass:

        public class MyClass extends Object
        {
            public const const_varable:int = 0;
            [Inject(desc="a public varable")]
            public var pub_varable:String = "";private var pri_varable:String = "";
            [Inject(desc="a static function")]
            public static function static_func(s:String):void
            {
                trace(s);
            }
            public function get accpri():String
            {
                return pri_varable;
            }
            public function set accpri(v:String):void
            {
                pri_varable = v;
            }
            public function TraceOut():void {
                trace(accpri);
            }
            public function MyClass()
            { 
            }
        }



    如果传入的参数是类型的实例,则返回的 XML 对象包括该类型的所有实例属性,但不包括任何静态属性。
    var m:MyClass = new MyClass();
    trace(describeType(m));
    得到如下的XML:

    <type name="MyClass" base="Object" isDynamic="false" isFinal="false" isStatic="false">
      <extendsClass type="Object"/>
      <constant name="const_varable" type="int">
        <metadata name="__go_to_definition_help">
          <arg key="pos" value="110"/>
        </metadata>
      </constant>
      <variable name="pub_varable" type="String">
        <metadata name="Inject">
          <arg key="desc" value="a public varable"/>
        </metadata>
        <metadata name="__go_to_definition_help">
          <arg key="pos" value="184"/>
        </metadata>
      </variable>
      <accessor name="accpri" access="readwrite" type="String" declaredBy="MyClass">
        <metadata name="__go_to_definition_help">
          <arg key="pos" value="425"/>
        </metadata>
        <metadata name="__go_to_definition_help">
          <arg key="pos" value="498"/>
        </metadata>
      </accessor>
      <method name="TraceOut" declaredBy="MyClass" returnType="void">
        <metadata name="__go_to_definition_help">
          <arg key="pos" value="571"/>
        </metadata>
      </method>
      <metadata name="__go_to_ctor_definition_help">
        <arg key="pos" value="632"/>
      </metadata>
      <metadata name="__go_to_definition_help">
        <arg key="pos" value="67"/>
      </metadata>
    </type>


    忽略掉其中__go_to_ctor_definition_help形式的metadata,可以看到如下XML:

    <type name="MyClass" base="Object" isDynamic="false" isFinal="false" isStatic="false">
      <extendsClass type="Object"/>
      <constant name="const_varable" type="int">
      </constant>
      <variable name="pub_varable" type="String">
        <metadata name="Inject">
          <arg key="desc" value="a public varable"/>
        </metadata>
      </variable>
      <accessor name="accpri" access="readwrite" type="String" declaredBy="MyClass">
      </accessor>
      <method name="TraceOut" declaredBy="MyClass" returnType="void">
      </method>
    </type>

    1.可以在解析 XML 对象时检查传入的是对象还是类,方法是检查 <type> 标签的 isStatic 属性的值,该值在 value 参数是类型的实例时为 false。

    2.private属性和方法不会被describeType函数解析到。


    要获取某个类型的静态属性,则传递该类型本身。返回的 XML 对象不仅仅包括类型的静态属性,而且还包括它的所有实例属性。
    trace(describeType(MyClass));忽略掉其中__go_to_ctor_definition_help形式的metadata,可以得到如下XML:

    <type name="MyClass" base="Class" isDynamic="true" isFinal="true" isStatic="true">
      <extendsClass type="Class"/>
      <extendsClass type="Object"/>
      <accessor name="prototype" access="readonly" type="*" declaredBy="Class"/>
      <method name="static_func" declaredBy="MyClass" returnType="void">
        <parameter index="1" type="String" optional="false"/>
        <metadata name="Inject">
          <arg key="desc" value="a static function"/>
        </metadata>
      </method>
      <factory type="MyClass">
        <extendsClass type="Object"/>
        <constant name="const_varable" type="int">
        </constant>
        <variable name="pub_varable" type="String">
          <metadata name="Inject">
            <arg key="desc" value="a public varable"/>
          </metadata>
        </variable>
        <accessor name="accpri" access="readwrite" type="String" declaredBy="MyClass">
        </accessor>
        <method name="TraceOut" declaredBy="MyClass" returnType="void">
        </method>
      </factory>
    </type>

    1.若传入参数为类,则实例属性嵌套在名为 <factory> 的标签内,从而与静态属性区分开来。在这种情况下,<type> 标签的 isStatic 属性为 true。

    2.所有的非静态方法将显示在<factory> 标签内,静态方法显示在<factory> 标签外。

    下表描述了由 describeType() 生成的 XML 对象的某些标签和属性(返回的所有类和接口名称均采用完全限定的格式):

    标签属性说明
    <type>   XML 对象的根标签。
      name ActionScript 对象的数据类型的名称。
      base ActionScript 对象的定义类的直接超类。如果 ActionScript 对象是类对象,则值为 Class
      isDynamic 如果 ActionScript 对象的定义类是动态的,则为 true;否则为 false。如果 ActionScript 对象是类对象,则值为 true,因为 Class 类是动态的。
      isFinal 如果 ActionScript 对象的定义类是最终类,则为 true;否则为 false
      isStatic 如果 ActionScript 对象是类对象或构造函数,则为 true;否则为 false。此属性之所以名为 isStatic,原因是:如果此属性为 true,则未嵌套在 factory 标记内的任何标签都是静态的。
    <extendsClass>   ActionScript 对象的定义类的每个超类都有一个单独的 extendsClass 标签。
      type ActionScript 对象的定义类扩展的超类的名称。
    <implementsInterface>   ActionScript 对象的定义类或其任何超类实现的每个接口都有一个单独的 implementsInterface 标签。
      type ActionScript 对象的定义类实现的接口的名称。
    <accessor>   存取器是 getter 和 setter 函数定义的一个属性。
      name 存取器的名称。
      access 属性的访问权限。可能的值包括 readonlywriteonlyreadwrite
      type 属性的数据类型。
      declaredBy 包含关联的 getter 或 setter 函数的类。
    <constant>   常量是用 const 语句定义的一个属性。
      name 常量的名称。
      type 常量的数据类型。
    <constructor>   在构造函数有参数时显示。
    <method>   方法是作为类定义的一部分声明的函数。
      name 方法的名称。
      declaredBy 包含方法定义的类。
      returnType 方法的返回值的数据类型。
    <parameter>   方法定义的每个参数都有一个单独的 parameter 标签。此标签始终嵌套在 <method> 标签内。
      index 一个数字,对应于参数在方法的参数列表中出现的顺序。第一个参数的值为 1。
      type 参数的数据类型。
      optional 如果参数是可选参数,则为 true;否则为 false
    <variable>   变量是用 var 语句定义的一个属性。
      name 变量的名称。
      type 变量的数据类型。
    <factory>   如果 ActionScript 对象是类对象或构造函数,则所有实例属性和方法均嵌套在此标签内。如果 <type> 标签的 isStatic 属性为 true,则未嵌套在 <factory> 标签内的所有属性和方法都是静态的。只有在 ActionScript 对象是类对象或构造函数时,此标签才会出现。
    <metadata>   用[]括起来的标签
      name 标签的名称。
    <arg>   标签中()括起来的参数
      key 参数的名称。
      value 参数的值。

     在debug编译条件下运行时我们能够正确获取metadata,但是release编译条件下会忽略未知的metadata,这时,需要在flex sdk的编译参数里面添加

    -keep-as3-metadata+=MyMetadata

    应用及API

    1.定义依赖

    Swiftsuspenders支持3种依赖定义:

    a.值绑定,将一个注入请求返回为一个给定的对象

    mapValue(whenAskedFor : Class, useValue : Object, named : String = "")

    b.类绑定,将一个注入请求返回为一个给定类的新建的实例

    mapClass(whenAskedFor : Class, instantiateClass : Class, named : String = "")

    c.单例绑定,将所有的注入请求返回为同一个共享的实例,这个实例由第一次请求时创建。

    mapSingleton(whenAskedFor : Class, named : String = "")

    public function mapSingletonOf(whenAskedFor : Class, useSingletonOf : Class, named : String = "")

    另外,Swiftsuspenders支持通过使用mapRule来扩展依赖映射

    当一个类型在多处被注入,且需要使用不同的注入依赖时,通过指定注入名的方式来区别各处的注入。

    2.定义注入点

    Swiftsuspenders支持在4个位置进行注入:

    a.属性(如setter)

    b.变量

    c.方法(支持可选参数)

    d.构造函数(支持可选参数)

    3.变量注入的用例

        public interface IClass 
        {
            function ClassInfo():String;
        }
    
        public class ClassA implements IClass
        {
            
            public function ClassA() 
            {
                
            }
            public function ClassInfo():String
            {
                return "Class A";
            }
        }
    
        public class ClassB  implements IClass
        {
            
            public function ClassB() 
            {
                
            }
            public function ClassInfo():String
            {
                return "Class B";
            }
        }
    
        public class MyClass
        {
            [Inject]
            public var iclass:IClass;
            
            public function MyClass() 
            {
                
            }
            public function TraceOut():void
            {
                trace(iclass.ClassInfo());
            }
            
        }
    
            var inj :Injector = new Injector();
    
            inj.mapClass(MyClass, MyClass);
            inj.mapClass(IClass, ClassA);
            var a:MyClass = inj.getInstance(MyClass);
            inj.unmap(IClass);
            inj.mapClass(IClass, ClassB);
            var b:MyClass = inj.getInstance(MyClass);
            a.TraceOut();
            b.TraceOut();

    4.构造函数注入的用例

     当对构造函数注入使用注入名时,元数据必须放在类定义的前面,而不是在构造函数前,这个是由于Flash Player的限制造成的。

    只需修改上面代码中的MyClass类:

        [Inject]
        public class MyClass
        {    
            public var iclass:IClass;
            
            
            public function MyClass(c:IClass) 
            {
                iclass = c;
            }
            public function TraceOut():void
            {
                trace(iclass.ClassInfo());
            }
            
        }

    5.其他

    a.可以使用PostConstruct来标注注入完成后自动调用的方法

    有些需要在注入完成后调用的方法,可以申明[PostConstruct]元数据,从而在所有的注入完成后,自动调用被[PostConstruct]申明的方法。可以通过order参数来决定方法调用的顺序,如:

    [PostConstruct(order=1)]

    b.可以用hasMapping来查询某依赖是否已经存在,若是需要重新映射已经存在的依赖,需要先使用unmap方法解除原有依赖。

    c.支持子注入器,通过createChildInjector方法实现。

    d.支持通过XML文件配置注入信息。

    设计结构

     

    SwiftSuspenders的类图如上图所示。整个框架以Injector为核心。InjectionPoint包中包含了所有与注入点相关的类,InjectionResult包中包含了返回策略相关的类。

    Injector中的m_mappings中包含所有的映射关系InjectionConfig,当调用mapClass,mapValue,mapSingleton时实际上就是建立类的映射关系,其序列图如下:

    m_injecteeDescriptions中包含InjecteeDescription对象,这个对象包括所有的注入点信息,包括构造注入点信息,和其他注入点信息。当getInstance时将根据之前配置的映射信息来获取所有注入点,再通过根据注入点进行注入来获取最终的对象,其流程图如下:

    这里面getInjectionPoints函数就是利用之前介绍的describeType函数来获取类的内容,并进行解析以获取注入点。

  • 相关阅读:
    2014年5月16日
    2014年4月8日
    Qt 小技巧之“To-Do 事项”
    koa中间件实现分析
    关于计算透视投影的四条边的方法,留作备忘
    关于向量
    关于ngui协同
    关于NGUI分辨率
    动态修改NGUI UI2DSprite
    动态设置viewport的宽高
  • 原文地址:https://www.cnblogs.com/studynote/p/3147801.html
Copyright © 2020-2023  润新知