什么是依赖注入

1 定义

依赖注入(Dependency Injection),简称DI,类之间的依赖关系由容器来负责。简单来讲a依赖b,但a不创建(或销毁)b,仅使用b,b的创建(或销毁)交给容器。

2 例子

为了把DI讲清楚,我们需要举一个简单例子。例子足够小,希望让你能直观的了解DI而不会陷入真实示例的泥潭。

例子:小明要杀怪,那小明拿什么武器杀怪呢?可以用刀、也可以用拳头、斧子等。

首先,我们创建一个演员类,名字叫“小明”,具有杀怪功能。

namespace NoInjection.ConsoleApp
{
    public class Actor
    {
        private string name = "小明";
        public void Kill()
        {
            var knife = new Knife();
            knife.Kill(name);
        }
    }
}

然后,我们再创建一个武器-刀类,具有杀怪功能。

using System;

namespace NoInjection.ConsoleApp
{
    public class Knife
    {
        public void Kill(string name)
        {
            Console.WriteLine($"{name}用刀杀怪");
        }
    }
}

最后,我们客户端调用演员类,执行杀怪功能。

using System;

namespace NoInjection.ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var actor = new Actor();
            actor.Kill();

            Console.ReadKey();
        }
    }
}

让我们来看看输出结果:

小明用刀杀怪

通过这个例子我们可以看到,Actor类依赖Knife类,在Actor中创建Knife,执行Knife.Kill方法。我们可以回顾一下DI的定义,a依赖b,但a不创建(或销毁)b,仅使用b,显然这个不符合DI做法。

DI下面我们详细说说DI的几种形式。

3 形式

3.1 构造函数注入

首先,我们在Actor通过构造函数传入Knife。

namespace ConstructorInjection.ConsoleApp
{
    public class Actor
    {
        private string name = "小明";
        private Knife knife;
        public Actor(Knife knife)
        {
            this.knife = knife;
        }

        public void Kill()
        {
            knife.Kill(name);
        }
    }
}

然后,Knife类不需要变化。

using System;

namespace ConstructorInjection.ConsoleApp
{
    public class Knife
    {
        public void Kill(string name)
        {
            Console.WriteLine($"{name}用刀杀怪");
        }
    }
}

最后,我们客户端来创建Actor和Knife,然后在Actor通过构造函数传入Knife。

using System;

namespace ConstructorInjection.ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var knife = new Knife();
            var actor = new Actor(knife);
            actor.Kill();

            Console.ReadKey();
        }
    }
}

让我们来看看输出结果:

小明用刀杀怪

这个例子我们可以看到,Actor类依赖Knife类,但在Actor不创建Knife,而是通过构造函数传入Knife。

3.2 Setter注入

首先,我们在Actor类创建Knife属性。

namespace SetterInjection.ConsoleApp
{
    public class Actor
    {
        private string name = "小明";
        private Knife knife;
        public Knife Knife
        {
            set 
            {
                this.knife = value;
            }
            get
            {
                return this.knife;
            }
        }

        public void Kill()
        {
            knife.Kill(name);
        }
    }
}

然后,Knife类不需要变化。

using System;

namespace SetterInjection.ConsoleApp
{
    public class Knife
    {
        public void Kill(string name)
        {
            Console.WriteLine($"{name}用刀杀怪");
        }
    }
}

最后,我们客户端来创建Actor和Knife,然后在Actor通过属性传入Knife。

using System;

namespace SetterInjection.ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var knife = new Knife();
            var actor = new Actor();
            actor.Knife = knife;
            actor.Kill();

            Console.ReadKey();
        }
    }
}

让我们来看看输出结果:

小明用刀杀怪

这个例子我们可以看到,Actor类依赖Knife类,但在Actor不创建Knife,而是通过属性传入Knife。

3.3 接口注入

首先,我们在Actor类创建Knife属性并继承IActor

namespace InterfaceInjection.ConsoleApp
{
    interface IActor
    {
        Knife Knife { set; get; }
        void Kill();
    }
}

namespace InterfaceInjection.ConsoleApp
{
    public class Actor: IActor
    {
        private string name = "小明";
        private Knife knife;
        public Knife Knife
        {
            set 
            {
                this.knife = value;
            }
            get
            {
                return this.knife;
            }
        }

        public void Kill()
        {
            knife.Kill(name);
        }
    }
}

然后,Knife类不需要变化。

using System;

namespace InterfaceInjection.ConsoleApp
{
    public class Knife
    {
        public void Kill(string name)
        {
            Console.WriteLine($"{name}用刀杀怪");
        }
    }
}

最后,我们客户端来创建Actor和Knife,然后在Actor通过属性传入Knife。

using System;

namespace InterfaceInjection.ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var knife = new Knife();
            IActor actor = new Actor();
            actor.Knife = knife;
            actor.Kill();

            Console.ReadKey();
        }
    }
}

接口注入方式我理解了也不是很透,感觉跟Setter注入没有什么大的差别,只是增加了一个接口定义。

posted @ 2021-01-10 11:07  南荣相如  阅读(12459)  评论(0编辑  收藏  举报