Unity 从零开始的框架搭建1-3 关于命令模式的一些思考-CSDN博客

        本人水平有限 如有不足还请斧正,该文章专栏是向QFrameWork作者凉鞋老师学习总结得来,吃水不忘打井人,不胜感激

        要说之前听都没听过这个东西 ,到底是什么东西?我试图学会它并分享出来

先说结论,IOC有以下作用:
       1.    依赖注入:通过构造函数、属性或方法将依赖对象注入到目标对象中,而不是在目标对象内部创建依赖对象
        2.    控制反转:将对象的创建和生命周期管理从应用程序代码中移到容器中,由容器来控制对象的创建和销毁

        其结构图如下:

0.场景 

        我先举一个例子,士兵和怪物都继承IWeapon类都可以开火

using UnityEngine;

public class NoIOCExample : MonoBehaviour
{
    private Solider0 solider0;
    private Enemy0 solider1;

    void Start() {
        solider0 = new Solider0();
        solider0.Fires();

        solider1 = new Enemy0();
        solider1.Fires();
    }
}

public interface IWeapon0 {
    void Fires();
}



public class Solider0 : IWeapon0 {
    public void Fires() {
        Debug.Log($"Soldier0 is Attack");
    } 
}
public class Enemy0 : IWeapon0 {
    public void Fires() {
        Debug.Log($"Enemy0 is Attack");
    }
}

1.不使用IOC

        1.我试图加几个士兵比如Solider1,Solider2,Solider3.......就会发现NoIOCExample类持有的士兵对象越来越多(当然怪物同理)

    这显然是一种高度的耦合

         2.我想要修改士兵的构造参数,我想它的无参改为有参构造比如:加一个伤害值

public class Solider0 : IWeapon0 {
    private int attck;
    Solider0(int attack){ 
        this.attck = attack;
    }
    public void Fires() {
        Debug.Log($"Soldier0 is Attack");
    } 
}

        然后所有关于该士兵实例化的地方都会报错:
         

        现在我将用所谓的IOC去解耦

2.IOC设计实例与解析

       其本质就是一个字典,如果不想那么泛用的话可以把Type改为其他具体类型

        然后其有两个方法:

        注册接口,定义了Input 与Output两种泛型,注意output是要继承Input的

        解析接口,通过传入的Input把Output转为Input(因为output是要继承Input的)

        关于  return (TInput)Activator.CreateInstance(implementationType);

        就是创建TOutput一个对象然后强转为TInput

using System;
using System.Collections.Generic;

public class IOC {
    private Dictionary<Type, Type> iocDict = new Dictionary<Type, Type>();

    /// <summary>
    /// 注册接口和实现类
    /// </summary>
    /// <typeparam name="TInput"></typeparam>
    /// <typeparam name="TOutput"></typeparam>
    public void Register<TInput, TOutput>() where TOutput : TInput {

        if(iocDict.ContainsKey(typeof(TInput)))
            iocDict[typeof(TInput)] = typeof(TOutput);
        else
            iocDict.Add(typeof(TInput), typeof(TOutput));
    }

    /// <summary>
    /// 解析接口
    /// </summary>
    /// <typeparam name="TInterface"></typeparam>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public TInput Resolve<TInput>() {
        var key = typeof(TInput);
        if (iocDict.TryGetValue(key, out var implementationType)) {
            return (TInput)Activator.CreateInstance(implementationType);
        }
        else {
            throw new Exception("未注册该接口"+implementationType);
        }
    } 
}

3.IOC作用体现

        控制反转(Inversion of Control)

       这个反转的意思是内部变成了外部

        原来:NoIOCExample(内部)Solider0Enemy0的创建具有完全的控制权

        反转:IOCExample只通过IOC容器的Resolve方法来获取已经创建好的IWeapon实例 ,控制权从使用依赖的对象(IOCExample)转移到了外部容器(IOC

        如果加许多士兵也不会让IOCExample 类持有大量的士兵对象了,而是直接让一个container疯狂去注册即可

using UnityEngine;

public class IOCExample : MonoBehaviour
{
    private IOC container;

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        container =new IOC();
        container.Register<IWeapon, Solider>();

        IWeapon weapon = container.Resolve<IWeapon>() ;

        weapon.Fire();
    }

}

public interface IWeapon {
    void Fire();
}


public class Solider :IWeapon {
    private int attck;
    public Solider(int attack) {
        this.attck = attack;
    }
    public void Fire() {
        Debug.Log($"Soldier0 is Attack");
    }
}

        依赖注入(Dependency Injection)

        在IOCExample类中,IOCExample依赖IWeapon接口

  IOC容器通过Register方法注册了IWeaponSolider的映射关系,并在Resolve方法中创建Solider实例(IWeapon的具体实现),然后将这个实例 “注入” 给IOCExample类(通过IOCExample调用Resolve方法获取),这里IOC容器扮演了注入者的角色,将IWeapon的实例注入到需要它的IOCExample类中,从而实现了依赖注入 

           现在我给士兵类加构造函数也不会报错,至于在哪里去给到这个attack,需要去修改IOC类,这里我给出一种解法

using System;
using System.Collections.Generic;

public class IOC
{
    private Dictionary<Type, Type> iocDict = new Dictionary<Type, Type>();
    private Dictionary<Type, object[]> constructorArgsDict = new Dictionary<Type, object[]>();

    /// <summary>
    /// 注册接口和实现类,无参数
    /// </summary>
    /// <typeparam name="TInput"></typeparam>
    /// <typeparam name="TOutput"></typeparam>
    public void Register<TInput, TOutput>() where TOutput : TInput
    {
        if (iocDict.ContainsKey(typeof(TInput)))
            iocDict[typeof(TInput)] = typeof(TOutput);
        else
            iocDict.Add(typeof(TInput), typeof(TOutput));
    }

    /// <summary>
    /// 注册接口和实现类,带构造函数参数
    /// </summary>
    /// <typeparam name="TInput"></typeparam>
    /// <typeparam name="TOutput"></typeparam>
    /// <param name="args">构造函数参数</param>
    public void Register<TInput, TOutput>(params object[] args) where TOutput : TInput
    {
        if (iocDict.ContainsKey(typeof(TInput)))
            iocDict[typeof(TInput)] = typeof(TOutput);
        else
            iocDict.Add(typeof(TInput), typeof(TOutput));
        constructorArgsDict[typeof(TInput)] = args;
    }

    /// <summary>
    /// 解析接口
    /// </summary>
    /// <typeparam name="TInput"></typeparam>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public TInput Resolve<TInput>()
    {
        var key = typeof(TInput);
        if (iocDict.TryGetValue(key, out var implementationType))
        {
            if (constructorArgsDict.TryGetValue(key, out var args))
            {
                return (TInput)Activator.CreateInstance(implementationType, args);
            }
            else
            {
                return (TInput)Activator.CreateInstance(implementationType);
            }
        }
        else
        {
            throw new Exception("未注册该接口" + implementationType);
        }
    }
}

4.你是否看出了最为关键的点? 

    就是这行代码: return (TInput)Activator.CreateInstance(implementationType);而其前提条件就是TOutput继承了TInput,这就是控制反转的核心

    依赖注入的核心就是Resolve方法的返回值,其将需要的类转为TInput的实例注入到了任意类之中

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐