StrangeIoC(一)
框架介绍
StrangeIoC是采用控制反转(IoC)思想书写的一个框架。
什么是控制反转呢?简单来说就是我们需要引用某一个类的实例时,不需要主动的获取(比如new一个实例然后对该实例引用,或者public之后在Unity的Inspector面板中拖拽),框架会自动把所需的实例传递给我们,StrangeIOC正是一个这样的框架。
在实际的项目中一个类可能会有多个实例,那么StrangeIOC如何知道我们想要的是哪一个实例呢?答案是通过“绑定”
现在看一个简单的例子:现在有类A、类B继承自Letter类,我们需要在客户端类Client中引用一个A的实例。
public class Letter
{
public virtual void SayHi()
{
print("I am Letter")
}
}
public class A:Letter
{
public override void SayHi()
{
print("I am A")
}
}
public class B:Letter
{
public override void SayHi()
{
print("I am B")
}
}
//不使用依赖注入
public class Client
{
public Letter letter;
public void Excute()
{
letter=new A();
letter.SayHi();
}
}
//使用依赖注入
public class Client
{
[Inject]
public Letter letter;
public void Excute()
{
letter.SayHi();
}
}
使用依赖注入时,如何将Client类中的letter和A类做一个绑定呢?我们只要在“某个特定的类”中写这样一句话:
Bind<Letter>().To<A>()
这样,我们需要Letter类的实例时,框架就会自动提供一个A的实例。(可能有读者想:我可能在某个地方引用Letter时想要一个B的实例呢,不要着急,以上代码不能真正运行,只是用来诠释原理,如何引用B会在后面讲到)
上面的“[Inject]”关键字是告诉程序,我这个地方的字段需要框架把具体实例放到这里。“某个特定的类”是指框架中的上下文类(上下文类可以理解为为程序正常运行提供一个环境的类,比如程序运行需要某些条件,上下文类可以创建这些条件)。
StrangeIOC的绑定类似一种键值对的形式,前面的键是输入,后面的值是输出。StrangeIOC为我们提供了以下几种绑定方式:
//泛型形式
Bind<Spaceship>().To<Liberator>();
//或者
Bind(typeof(Spaceship)).To(typeof(Liberator));
//值形式
Bind(“MeaningOfLife”).To(42);
//混合形式
Bind<Spaceship>().To(“Enterprise”);
现在基本上介绍完了。。。是不是毫无框架的感觉。。。上面的内容可以说是StrangeIOC框架的核心,好在框架作者在核心的基础上为我们扩展了一个MVCS版本的StrangeIOC框架。
[Inject]和上下文
在MVCS版本中,“[Inject]”只能用于属性和索引器,所以上面的客户端的写法应该是:
public class Client
{
[Inject]
public Letter letter{get;set;};
public void Excute()
{
letter.SayHi();
}
}
上下文在该版本中对应于的MVCSContext类;
绑定
在该版本中,作者为我们扩展了三种绑定:injectionBinder
,mediationBinder
,commandBinder
。
-
injectionBinder
- 实例注入绑定。我们需要实例的需要以这种方式添加绑定。有以下几种绑定方式:
//方式一,绑定到A的单例
injectionBinder.Bind<Letter>().To<A>().ToSingleton();
//方式二,绑定到A的某个特定实例
A instanceA=GameObject.Find("A").GetComponent<A>();//获取A的特定实例
injectionBinder.Bind<Letter>().ToValue(instanceA);
//方式三,创建一个A的实例并绑定
injectionBinder.Bind<Letter>().To<A>();
我们也可以为绑定添加名称用以区分不同的绑定,比如上面绑定Letter的例子可以改为:
injectionBinder.Bind<Letter>().To<A>().ToSingleton().ToName("NameA");
injectionBinder.Bind<Letter>().To<B>().ToName("NameB");
这样,我们在使用时通过给定不同的名称用以区分不同的实例:
//在客户端中
public class Client
{
[Inject("NameA")]
public Letter letterA{get;set;}
[Inject("NameB")]
public Letter letterA{get;set;}
public void ExcuteA()
{
letterA.SayHi();
}
public void ExcuteB()
{
letterB.SayHi();
}
}
也可以一次绑定多个:
injectionBinder.Bind<Letter>().Bind<Word>().To<A>().ToSingleton().ToName("NameA");
这样,我们在某个地方引用Letter或者Word时都会得到A的单例。
在上面的三种方式中,除了方式二,我们需要的实例都是StrangeIOC框架自动创建的,当存在多个构造函数时,框架优先使用使用“[Construct]”修饰的构造函数,如果不存在,则默认使用参数最少的构造函数。
public class A:Letter
{
public A()
{
//默认使用这个构造函数
}
[Construct]
public A(string arg)
{
//但是使用[Construct]标签的话,Strange会使用这个构造函数
}
}
如果使用属性注入的话,[PostConstruct]将会是一个非常有用的标签. 任何使用[PostConstruct]标签的方法在属性注入后会被立即调用. 通过这种方式可以在属性注入完成后使用属性, 这是一个安全类型不会返回空指针.
[PostConstruct]
public void PostConstruct()
{
//在这里处理一些一般在构造函数里面处理的事
}
多个使用[PostConstruct]的方法可以按下面的方式按序执行
[PostConstruct(1)]
public void PostConstructOne()
{
//第一个执行
}
[PostConstruct(2)]
public void PostConstructTwo()
{
//第二个执行
}