Unity StrangeIOC MVCS框架

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类图

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

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值