StrangeIOC
基本概念
控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
StrangeIOC基础类型
1. Context
- 上下文组件定义程序边界,也就是可以把一个程序定义成多上下文,让代码更加模块化
- 它提供了程序入口,也算是框架中耦合度最高的地方
2. Binder和Binding
- 这两个类是这个框架最重要的组成部分
- Binding存储了对象的绑定关系,而Binder存储了Binding的对象
3. View和Mediator
- MVCS中的View层,View只用于显示,也就是View只负责管理UI,Mediator负责界面逻辑,事件响应等
4. Model
- MVCS中的Model层,负责数据部分
5. Command
- MVCS中的Control层,负责执行逻辑代码
6. Service
- MVCS中的Service层,负责与第三方交互,这个Service并不是一定指代服务器,也可以是其他的软件,什么都可以,它就是程序对外的接口
7. Dispatcher
- 派发器是框架内通信主线的其中一种,用来派发消息,触发命令,从而进一步解耦
8. Signal
- 信号是框架内另外一种通信主线,它采用强类型,来绑定信号和命令之间的关系,实现消息响应的触发
9. ReflectionBinder
- 反射部分,通过binding来获取类的信息,存储在ReflectedClass中
10. Injector
- 注入器,通过反射获取的信息,来实例化请求的对象
StrangeIOC类图
![](https://i-blog.csdnimg.cn/blog_migrate/d504feb23f7c471b1575965d800e5110.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/d463f9e51685865951bfa052682b5b63.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/7997a10f7b23399b6ba3518c84652ac3.jpeg)
StrangeIOC的执行流程(Signal)
1. SignalsContext
-
SignalsContext是创建的上下文类型,继承自MVCSContext,会在我们自建创建的SignalsRoot : ContextView中执行,这就是程序的唯一入口
public class SignalsRoot : ContextView { void Awake() { context = new SignalsContext(this); } }
-
SignalsContext的基类Context的构造中,调用了addCoreComponents与Start两个方法,其中Start方法,是框架初始化的重要方法
-
如果是v0.6需要取消默认EventCommandBinder并重新绑定SignalCommandBinder
protected override void addCoreComponents() { base.addCoreComponents(); injectionBinder.Unbind<ICommandBinder>(); injectionBinder.Bind<ICommandBinder>().To<SignalCommandBinder>().ToSingleton(); }
-
重写Start发起请求信号
public override IContext Start() { base.Start(); StartSignal startSignal = injectionBinder.GetInstance<StartSignal>(); startSignal.Dispatch(); return this; }
2. Signal
-
信号是Strange内的一个通讯工具,通过信号可以添加监听,也可以派发消息事件。
-
信号最多支持4个泛型参数,信号可以绑定不同或者相同的类型。
//开始信号 public class StartSignal : Signal { } //使用 [Implements(typeof(TestSignal),InjectionBindingScope.CROSS_CONTEXT)] 绑定到 CROSS_CONTEXT public class TestSignal: Signal<string,string,int,bool> { }
3. Command
-
命令在Strange内定义是程序书写逻辑的地方。每个命令就像是一个独立的方法。命令通常没有状态,意味着它不保留任何信息。只管做他自己需要做的事,然后结束。命令核心的方法就是Execute,当命令被执行的时候,Excute方法就被调用执行。
-
命令可以做任何事情,更新场景物体,更新服务,更新模型,升级等等。
public class StartCommand : Command { public override void Execute(){ //Retain(); } }
-
Retain() : 命令一般执行完事生命周期就结束了,但是对一些需要时间等待的命令就会有一些问题。例如我向服务器请求一个数据列表,我刚执行没等服务器给我反馈命令的生命周期就结束了。Retain方法就是来保持Command的生命周期。当你接收到服务器的反馈后,再执行Release()函数来释放命令的生命周期。
4. View and Mediator
- View : 挂载到GameObject上,View脚本会执行它自己的Awake()和Start()。
这两个方法中,都调用了bubbleToContext方法,View使用这个函数,把自己添加到当前上下文中 - Mediator :负责界面逻辑,事件响应等。
OnRegister() 是注入后立即触发的方法。它就像一个构造函数,你可以使用它来初始化视图,并执行其他初始化过程。
OnRemove() 是为清理添加过的监听
5. Bingding
-
绑定是Strange的核心,我们可以将一个或多个对象与另外一个或多个对象绑定(连接)在一起,将接口与类绑定来实现接口,将事件与事件接收绑定在一起。或者绑定两个类,一个类被创建时另一个类自动创建。
-
一个Strange的绑定由两个必须部分和一个可选部分组成。必须的部分是一个key和一个value。Key触发value,就像一个事件可以触发一个回调。或一个类的实例化可以导致另一个类的实例化。可选部分是一个名字。在一些情况下,我们会使用相同的键来限定不同两个的绑定。在这种情况下,这个名字作为一个鉴别器。
-
重写Context的mapBindings
-
injectionBinder :用于绑定实体模型类。
-
mediationBinder :用于绑定视图与视图调制器,视图需继承View,调制器继承Mediator
-
commandBinder :用于绑定命令
protected override void mapBindings() { injectionBinder.Bind<IHit>().To<Hit>(); injectionBinder.Bind<ApplicationQuitSignal>().ToSingleton().CrossContext(); mediationBinder.Bind<LoadSceneView>().To<LoadSceneMediator>(); commandBinder.Bind<GameStartSignal>().To<GameStartCommand>().ToName(ContextKeys.TEST); commandBinder.Bind<GameStartSignal>().To<LoadAndConfigureLevelCommand>(); commandBinder.Bind<GameStartSignal>() .To<LoadLevelCommand>() .To<ConfigureLevelCommand>() .To<GameStartCommand>(). .InSequence(); commandBinder.Bind<GameStartSignal>().To<LoadAndConfigureLevelCommand>().Once(); }
-
InSequence() : 信号所绑定的各个命令之间的执行顺序为依次执行,前一个执行完再执行下一个,直到完全结束。若不加InSequence,那么代表命令为并行执行的关系
-
Once() : 当前命令仅执行一次,执行完成后,再也不执行此操作。适用于一些初始化的操作。例如文件读取,程序配置等(重置功能不要使用此方法)
-
CrossContext() : 绑定到 CROSS_CONTEXT 跨Context访问
6. Inject
-
通过[Inject]注入属性
[Inject] public ApplicationQuitSignal applicationQuitSignal { get; set; } [ListensTo(typeof(ApplicationQuitSignal))] void ApplicationQuit() { }
核心部分
Context
-
初始化
public Context(object view, ContextStartupFlags flags) { //If firstContext was unloaded, the contextView will be null. Assign the new context as firstContext. if (firstContext == null || firstContext.GetContextView() == null) { firstContext = this; } else { firstContext.AddContext(this); } SetContextView(view); addCoreComponents(); //添加核心组件的绑定关系 this.autoStartup = (flags & ContextStartupFlags.MANUAL_LAUNCH) != ContextStartupFlags.MANUAL_LAUNCH; if ((flags & ContextStartupFlags.MANUAL_MAPPING) != ContextStartupFlags.MANUAL_MAPPING) { Start(); //初始化 } }
-
Start方法,是框架初始化的重要方法
virtual public IContext Start() { instantiateCoreComponents(); //实例化核心组件 mapBindings(); postBindings(); if (autoStartup) Launch(); return this; }
-
如果有需要重写Start方法的地方,不要忘记base.Start();
injectionBinder
-
这个Binder对象是在CrossContext中的属性中,自己new出来的,并没有依靠依赖注入
public ICrossContextInjectionBinder injectionBinder { get { return _injectionBinder ?? (_injectionBinder = new CrossContextInjectionBinder()); } set { _injectionBinder = value; } }
-
在CrossContextInjectionBinder的基类InjectionBinder中可以看到注入部分所需的组件,都是在它的构造中自动生成的
public InjectionBinder () { injector = new Injector (); injector.binder = this; injector.reflector = new ReflectionBinder(); }
-
injectionBinder是框架所有组件初始化的关键,其他的核心组件的实例化都是由它来完成的
View和Mediator
-
View和Mediator比较特殊,因为是要挂载到GameObject上的
-
View脚本会执行它自己的Awake()和Start()
-
这两个方法中,都调用了bubbleToContext方法,把自己添加到当前上下文中(context.AddView(view))
-
在AddView中,调用mediationBinder的Trigger
public void Trigger(MediationEvent evt, IView view) { Type viewType = view.GetType(); IMediationBinding binding = GetBinding (viewType) as IMediationBinding; if (binding != null) { switch(evt) { case MediationEvent.AWAKE: InjectViewAndChildren(view); MapView (view, binding); break; case MediationEvent.DESTROYED: UnmapView (view, binding); break; case MediationEvent.ENABLED: EnableView (view, binding); break; case MediationEvent.DISABLED: DisableView (view, binding); break; default: break; } } else if (evt == MediationEvent.AWAKE) { //Even if not mapped, Views (and their children) have potential to be injected InjectViewAndChildren(view); } }
-
InjectViewAndChildren就是用来实现注入操作的 - injectionBinder.injector.Inject
protected virtual void InjectViewAndChildren(IView view) { IView[] views = GetViews(view); int aa = views.Length; for (int a = aa - 1; a > -1; a--) { IView iView = views[a] as IView; if (iView != null && iView.shouldRegister) { if (iView.autoRegisterWithContext && iView.registeredWithContext) { continue; } iView.registeredWithContext = true; if (iView.Equals(view) == false) Trigger(MediationEvent.AWAKE, iView); } } injectionBinder.injector.Inject(view, false); }
-
MapView用来添加Mediator
virtual protected void MapView(IView view, IMediationBinding binding) { Type viewType = view.GetType(); if (bindings.ContainsKey (viewType)) { object[] values = binding.value as object[]; int aa = values.Length; for (int a = 0; a < aa; a++) { Type mediatorType = values [a] as Type; if (mediatorType == viewType) { throw new MediationException(viewType + "mapped to itself. The result would be a stack overflow.", MediationExceptionType.MEDIATOR_VIEW_STACK_OVERFLOW); } ApplyMediationToView (binding, view, mediatorType); if (view.enabled) EnableMediator(view, mediatorType); } } } protected virtual void ApplyMediationToView(IMediationBinding binding, IView view, Type mediatorType) { bool isTrueMediator = IsTrueMediator(mediatorType); if (!isTrueMediator || !HasMediator(view, mediatorType)) { Type viewType = view.GetType(); object mediator = CreateMediator(view, mediatorType); if (mediator == null) ThrowNullMediatorError (viewType, mediatorType); if (isTrueMediator) ((IMediator)mediator).PreRegister(); Type typeToInject = (binding.abstraction == null || binding.abstraction.Equals(BindingConst.NULLOID)) ? viewType : binding.abstraction as Type; injectionBinder.Bind(typeToInject).ToValue(view).ToInject(false); injectionBinder.injector.Inject(mediator); injectionBinder.Unbind(typeToInject); if (isTrueMediator) { ((IMediator)mediator).OnRegister(); } } }
commandBinder(Signal)
-
信号触发初始化(Bing<>)
virtual public IBinding Bind(object key) { IBinding binding; binding = GetRawBinding (); binding.Bind(key); return binding; } virtual public IBinding GetRawBinding() { return new Binding (resolver); }
-
GetRawBinding() : 生成新的binding对象,来存储传进来的key值,而在生成binding的过程中,还绑定了这个binding的resolver(分析器)
-
这个resolver中,执行了ResolveBinding方法
virtual protected void resolver(IBinding binding) { object key = binding.key; if (binding.keyConstraint.Equals(BindingConstraintType.ONE)) { ResolveBinding (binding, key); } else { object[] keys = key as object[]; int aa = keys.Length; for(int a = 0; a < aa; a++) { ResolveBinding (binding, keys[a]); } } }
-
而SignalCommandBinder的ResolveBinding方法,给提供的signal绑定了ReactTo方法
override public void ResolveBinding(IBinding binding, object key) { base.ResolveBinding(binding, key); if (bindings.ContainsKey(key)) //If this key already exists, don't bind this again { IBaseSignal signal = (IBaseSignal)key; signal.AddListener(ReactTo); //Do normal bits, then assign the commandlistener to be reactTo } }
-
ReactTo
virtual public void ReactTo(object trigger, object data) { if (data is IPoolable) { (data as IPoolable).Retain (); } ICommandBinding binding = GetBinding (trigger) as ICommandBinding; if (binding != null) { if (binding.isSequence) { next (binding, data, 0); } else { object[] values = binding.value as object[]; int aa = values.Length + 1; for (int a = 0; a < aa; a++) { next (binding, data, a); } } } }
-
next
protected void next(ICommandBinding binding, object data, int depth) { object[] values = binding.value as object[]; if (depth < values.Length) { Type cmd = values [depth] as Type; ICommand command = invokeCommand (cmd, binding, data, depth); ReleaseCommand (command); } else { disposeOfSequencedData (data); if (binding.isOneOff) { Unbind (binding); } } }
-
invokeCommand 中 executeCommand 执行Command的Execute方法
virtual protected ICommand invokeCommand(Type cmd, ICommandBinding binding, object data, int depth) { ICommand command = createCommand (cmd, data); command.sequenceId = depth; trackCommand (command, binding); executeCommand (command); return command; }
注入流程
-
实现注入就需要使用injectionBinder对象,有两种实现注入的流程
-
一种是向在AbstractMediationBinder中一样直接调用
protected virtual void ApplyMediationToView(IMediationBinding binding, IView view, Type mediatorType) { bool isTrueMediator = IsTrueMediator(mediatorType); if (!isTrueMediator || !HasMediator(view, mediatorType)) { Type viewType = view.GetType(); object mediator = CreateMediator(view, mediatorType); if (mediator == null) ThrowNullMediatorError (viewType, mediatorType); if (isTrueMediator) ((IMediator)mediator).PreRegister(); Type typeToInject = (binding.abstraction == null || binding.abstraction.Equals(BindingConst.NULLOID)) ? viewType : binding.abstraction as Type; injectionBinder.Bind(typeToInject).ToValue(view).ToInject(false); injectionBinder.injector.Inject(mediator); //注入 injectionBinder.Unbind(typeToInject); if (isTrueMediator) { ((IMediator)mediator).OnRegister(); } } }
-
或者像CommandBinder这样 - injectionBinder.GetInstance()
protected ICommand getCommand(Type type) { if (usePooling && pools.ContainsKey(type)) { Pool pool = pools [type]; ICommand command = pool.GetInstance () as ICommand; if (command.IsClean) { injectionBinder.injector.Inject (command); command.IsClean = false; } return command; } else { injectionBinder.Bind<ICommand> ().To (type); ICommand command = injectionBinder.GetInstance<ICommand>(); injectionBinder.Unbind<ICommand> (); return command; } }
-
GetInstance() - GetInjectorForBinding(binding).Instantiate(binding, false)
-
获取到Injector对象以后,调用Instantiate方法
public virtual object GetInstance(Type key, object name) { IInjectionBinding binding = GetBinding (key, name) as IInjectionBinding; if (binding == null) { throw new InjectionException ("InjectionBinder has no binding for:\n\tkey: " + key + "\nname: " + name, InjectionExceptionType.NULL_BINDING); } object instance = GetInjectorForBinding(binding).Instantiate (binding, false); injector.TryInject(binding,instance); return instance; } protected virtual IInjector GetInjectorForBinding(IInjectionBinding binding) { return injector; }
-
而在Inject方法和Instantiate方法中,都有这句代码
IReflectedClass reflection = reflector.Get(t)
-
这句就是通过类型,获取反射信息的代码,获取反射信息之后在Inject中,调用了这样两个方法
//注入属性 performSetterInjection(target, reflection); //输入方法 postInject(target, reflection);
参考1:https://blog.csdn.net/zcaixzy5211314/article/details/80876228
参考2:https://blog.csdn.net/ljn398431/article/details/106445734?spm=1001.2014.3001.5501