StangeIOC Signal and Command 信号和命令
信号和命令(Signals and Commands)
信号和命令在Strange里是两个独立的体系,通过绑定来将两者结合到一起。
信号(Signals)
信号是Strange内的一个通讯工具。通过信号可以添加监听,也可以派发消息事件。
public class LevelBeginSignal : Signal<int>{};
LevelBeginSignal signal = new LevelBeginSignal();
signal.AddListener(LevelBeginHandler);
public void LevelBeginHandler(int level)
{
Debug.Log("We're beginning Level " + level);
}
signal.Dispatch(6); //Logs "We're beginning Level 6"
信号最多支持4个泛型参数,信号可以绑定不同或者相同的类型。
public class ButtonClickedSignal : Signal{};
public class GameObjectNameChangeSignal : Signal< GameObject, string>{};
public class BandAgeUpdateSignal : Signal<int, int, int, int>{};
ButtonClickedSignal.Dispatch();
GameObjectNameChangeSignal.Dispatch(gameObject, newName);
BandAgeUpdateSignal.Dispatch(19, 22, 20, 22);
如果你想要使用更多的参数,可以使用数据结构体或者类的形式来进行传参操作。
信号若是加上Strange框架内的依赖注入功能,那么就可以有很神奇的功能。
injectionBinder.Bind<LevelBeginSignal>().ToSingleton();
public class SomeClass
{
[Inject]
public LevelBeginSignal signal { get; set; }
[PostConstruct]
public void PostConstruct()
{
signal.AddListener(LevelBeginHandler);
}
private void LevelBeginHandler(int level)
{
Debug.Log("SomeClass received level " + level);
}
}
public class SomeOtherClass
{
[Inject]
public LevelBeginSignal signal { get; set; }
[PostConstruct]
public void PostConstruct()
{
signal.AddListener(LevelBeginHandler);
}
private void LevelBeginHandler(int level)
{
Debug.Log("SomeOtherClass received level " + level);
}
}
public class YetAnotherClass
{
[Inject]
public LevelBeginSignal signal { get; set; }
public void SomethingHappened()
{
signal.Dispatch(42); //Logs "SomeClass received level 42" and
//"SomeOtherClass received level 42)"
}
}
LevelBeginSignal信号通过绑定操作之后,通过依赖注入,那么我可以在任意类内注入信号,并且对信号进行操作。上面代码示例中,我在SomeClass和SomeOtherClass中添加了对LevelBeginSignal的监听,然后YetAnotherClass中发送开始42关的消息,SomeClass和SomeOtherClass都收到了开始42关的消息,执行了响应的监听函数。
命令(Commands)
命令在Strange内定义是程序书写逻辑的地方。每个命令就像是一个独立的方法。命令通常没有状态,意味着它不保留任何信息。只管做他自己需要做的事,然后结束。命令核心的方法就是Execute,当命令被执行的时候,Excute方法就被调用执行。
public class MyCommand : Command
{
override public Execute()
{
Debug.Log("By your Command");
}
}
命令可以做任何事情,更新场景物体,更新服务,更新模型,升级等等。命令的关键点是你程序内的动作。
Retain()
命令一般执行完事生命周期就结束了,但是对一些需要时间等待的命令就会有一些问题。例如我向服务器请求一个数据列表,我刚执行没等服务器给我反馈命令的生命周期就结束了。Retain方法就是来保持Command的生命周期。当你接收到服务器的反馈后,再执行Release()函数来释放命令的生命周期。
public class MyCommand : Command
{
[Inject]
public IService service { get; set; }
override public Execute()
{
Retain();
service.CallOutToAServer().Then(DoAnotherThing);
}
private void DoAnotherThing()
{
Release();
}
}
上面方法中涉及到Promise的用法,这个是Strange拓展的一个功能,功能参照Promise/A规范。
下面说说咱们文章的关键了。
信号命令的绑定
public class MySignal : Signal<string>{}
public class MyCommand : Command
{
[Inject]
public string MyString { get; set; }
override public Execute()
{
Debug.Log(MyString);
}
}
commandBinder.Bind<MySignal>().To<MyCommand>();
上面例子最后一行代码中实际上 是做了三件事
1.将信号绑定为单例,这样它就可以在任意地方进行注入操作。
2.实例化并执行命令。
3.信号可以将参数注入到命令中。
示例中的 MyString就是信号的参数,Strange底层进行了对此数据的绑定操作,无需手动进行其他操作。
这样我们就把实现下面的示例,通过命令来执行了。
public class MyClass
{
[Inject]
public MySignal signal { get; set; }
public PostConstruct()
{
signal.Dispatch("Hello Command"); //Logs: "Hello Command";
}
}
通过这样就可以实现解耦操作了,可能类的数量会增加很多,信号和命令,但是相互之间的关系独立起来了。
commandBinder.Bind<GameStartSignal>().To<GameStartCommand>();
commandBinder.Bind<GameStartSignal>().To<LoadAndConfigureLevelCommand>();
commandBinder.Bind<GameStartSignal>()
.To<LoadLevelCommand>()
.To<ConfigureLevelCommand>()
.To<GameStartCommand>().
.InSequence() ;
commandBinder.Bind<GameStartSignal>().To<LoadAndConfigureLevelCommand>().Once();
上面示例中前两行代码都是绑定GameStartSignal信号,但是这样的写法可能比较繁琐。而且阅读起来也很差,并且相互之间的执行顺序可能也并不清晰。
再下面的一行可能大家发现了一个新的函数InSequence,这个函数是代表信号所绑定的各个命令之间的执行顺序为依次执行,前一个执行完再执行下一个,知道完全结束。若不加InSequence,那么代表命令为并行执行的关系。
最后一行中有一个Once的函数,此函数代表当前命令仅执行一次,执行完成后,再也不执行此操作。适用于一些初始化的操作。例如文件读取,程序配置等(重置功能不要使用此方法)。
注意事项
信号绑定命令的时候,参数不可以为同一类型的,若是如此,在注入命令中的时候会报错。