C#--依赖注入

小编在之前面试某家企业时,面试官提出了一个问题,虽然按经验,说出了解决方法,面试官也表示此方法可以实现,且提出了另一种方案,就是“依赖注入”。

面试官的题目是这样子的:如果现在公司有个程序,假设是关于动物园的程序,有些许动物(动物库A)已经加到了动物园里了,里面的动物都完成了动物该有的行为,然后程序已经测试通过,没有问题并已经发布。现在问题是,动物园里要添加新的动物种类,请问,如何不修改动物库A的代码的情况下,将新的动物物种添加到动物园里(题目大概就是这个意思,无非就是说,原来程序不允许修改,只能新增加别的类库,并完成原来的那个类库中的行为,差不多就是这个意思的啦!)。

小编给的方案是,使用反射,好了,这个不是今天的话题,不做详细方案介绍。而面试官提出的方案就是----依赖注入。

面试回来后,总结面试中遇到的问题,其中就对依赖注入进行了一下研究,还是收获很多,以下就是研究这个依赖注入的DEMO。

先看解决方案架构,是个控制台程序

主程序是Zoo,BLL是执行动物的行为,动物库A就是LocalAnimal,其实就是我们熟知的三层架构。有过一定经验的人都应该知道,普通的三层架构中,UI(Zoo)依赖于->BLL依赖于->DAL(LocalAnimal),而这样的设计会导致每一层之间存在很强的耦合关系。点到为止,不详细说了,不是此篇的重点。

我们先看LocalAnimal

先是IAnimal这个接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LocalAnimal
{
    /// <summary>
    /// 动物接口类
    /// </summary>
    public interface IAnimal
    {
        void Voice();  //叫声
    }
}

 接口很简单,里面就是动物应该所拥有的行为,此处定义了叫声

OK,现在看看Animal文件夹下的动物们

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LocalAnimal.Animal
{
    /// <summary>
    /// 白鳍豚
    /// </summary>
    public class White_Flag_Dolphin:IAnimal
    {
        public void Voice()
        {
            Console.WriteLine("吱吱吱");
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LocalAnimal.Animal
{
    /// <summary>
    /// 熊猫
    /// </summary>
    public class Panda:IAnimal
    {
        public void Voice()
        {
            Console.WriteLine("嚯嚯嚯");
        }
    }
}

每种动物都实现了动物接口的叫声方法,其中熊猫的叫声是“嚯嚯嚯”(小编也没见过熊猫,不知道怎么叫的,随便写的),白鳍豚的叫声是“吱吱吱”(大概是这样吧)

OK,现在为止,这些动物都是存在的了,但是要放到动物园里,并且让他们开心的叫起来啊!

先看看业务(BLL,动物叫)里的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LocalAnimal;
using LocalAnimal.Animal;

namespace BLL
{
    public class AnimalVoice
    {
        IAnimal _iAnimal = null;
        public AnimalVoice(IAnimal iAnimal)
        {
            this._iAnimal = iAnimal;  //构造注入
        }
        public void Voice()
        {
            _iAnimal.Voice();
        }
    }
}

此处就是重点了,实现依赖注入的方法之一是构造注入,也就是使用构造函数进行依赖项注入。

看UI(Zoo)中的代码

using System.Text;
using LocalAnimal;
using LocalAnimal.Animal;
using BLL;

namespace Zoo
{
    class Program
    {
        static void Main(string[] args)
        {
            //动物园里引进了熊猫
            //IAnimal iAnimal = new Panda();
            //AnimalVoice animalVoice = new AnimalVoice(iAnimal);
            //animalVoice.Voice();

            //动物园里引进了白鳍豚
            IAnimal iAnimal = new White_Flag_Dolphin();
            AnimalVoice animalVoice = new AnimalVoice(iAnimal);
            animalVoice.Voice();

            Console.ReadKey();
        }
    }
}

执行结果

到这里,我们可以看到,无论我们将来需要动物园中增加多少动物,只需要在动物库(LocalAnimal)中增加该动物,并且在Zoo中实例化该动物,就能开始完成动物的业务(叫),而业务层(BLL)中的代码完全不需要改变。

OK,开始完成本篇开始面试官给我出的面试题。

如题描述,如果LocalAnimal中的代码已经完成编码并关闭,已经不允许在里面做任何的改动,则此时,如果动物园要引入新的动物怎么办呢?没问题,接下来,可以看到面向接口开发以及使用依赖注入解耦的优点。

我们新建一个类库,取名为ForeignAnimal(相当于引入了新的.dll,这也是面试官想看到,我们新增加了一个扩展文件,而不需要修改它的LocalAnimal),如下图

 ForeignAnimal中引入了LocalAnimal,其目的是为了实现LocalAnimal中的IAnimal接口,以完成对BLL中的依赖注入,看袋鼠(Kangaroo类)的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LocalAnimal;

namespace ForeignAnimal.NewAnimal
{
    /// <summary>
    /// 袋鼠
    /// </summary>
    public class Kangaroo : IAnimal
    {
        public void Voice()
        {
            Console.WriteLine("咚咚咚");
        }
    }
}

 与Panda和White_Flag_Dolphin中的代码完全一致,没有任何不同。那现在Zoo中要开始引入袋鼠了,此时,先在Zoo中引入ForeignAnimal,如下图

引入 ForeignAnimal的动态库后,Zoo开始引进袋鼠,如下代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LocalAnimal;
using LocalAnimal.Animal;
using BLL;
using ForeignAnimal.NewAnimal;

namespace Zoo
{
    class Program
    {
        static void Main(string[] args)
        {
            //动物园里引进了熊猫
            //IAnimal iAnimal = new Panda();
            //AnimalVoice animalVoice = new AnimalVoice(iAnimal);
            //animalVoice.Voice();

            //动物园里引进了白鳍豚
            //IAnimal iAnimal = new White_Flag_Dolphin();
            //AnimalVoice animalVoice = new AnimalVoice(iAnimal);
            //animalVoice.Voice();

            //动物园里引进了袋鼠
            IAnimal iAnimal = new Kangaroo();
            AnimalVoice animalVoice = new AnimalVoice(iAnimal);
            animalVoice.Voice();

            Console.ReadKey();
        }
    }
}

执行结果

执行结果,也没问题,并且也可以看到,我们完全没有动过业务层(BLL)的代码。

到这里,就完成面试官提出的问题,我在完全没有修改LocalAnimal的情况下,新增了ForeignAnimal,并且将ForeignAnimal中的动物成功引进到了Zoo中,这就叫--依赖注入。

现在,如果我们看一下代码,我们只依赖于业务访问层中数据访问层的抽象,而业务访问层是使用的是数据访问层实现的接口。因此,我们遵循了更高层次对象和更低层次对象都依赖于抽象的原则,抽象是更高层次对象和更低层次对象之间的契约。

现在,有同学又要说了,“不行啊!我能不能也不修改Zoo中的代码呢?”,答案是可以的,此时需要用到反射,代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LocalAnimal;
using LocalAnimal.Animal;
using BLL;
using ForeignAnimal.NewAnimal;
using System.Reflection;

namespace Zoo
{
    class Program
    {
        static void Main(string[] args)
        {
            //动物园里引进了熊猫
            //IAnimal iAnimal = new Panda();
            //AnimalVoice animalVoice = new AnimalVoice(iAnimal);
            //animalVoice.Voice();

            //动物园里引进了白鳍豚
            //IAnimal iAnimal = new White_Flag_Dolphin();
            //AnimalVoice animalVoice = new AnimalVoice(iAnimal);
            //animalVoice.Voice();

            //动物园里引进了袋鼠
            //IAnimal iAnimal = new Kangaroo();
            //AnimalVoice animalVoice = new AnimalVoice(iAnimal);
            //animalVoice.Voice();

            Assembly assembly = Assembly.LoadFrom("ForeignAnimal.dll");  //如果是需要LocalAnimal,则此处字符串改成 LocalAnimal.dll 即可
            IAnimal iAnimal = (IAnimal)assembly.CreateInstance("ForeignAnimal.NewAnimal.Kangaroo", true);
            AnimalVoice animalVoice = new AnimalVoice(iAnimal);
            animalVoice.Voice();

            Console.ReadKey();
        }
    }
}

执行结果

结果也没问题。 

如下图,红框中的字符串均可写入到配置文件中,这样的话,无论新增了多少动态库、类,只需要在配置文件中做新增修改就可以,Zoo中的代码无需再修改。

总结

依赖注入的优点:

1、传统的代码,每个对象负责管理与自己需要依赖的对象,导致如果需要切换依赖对象的实现类时,需要修改多处地方。同时,过度耦合也使得对象难以进行单元测试。

2、依赖注入把对象的创造交给外部去管理,很好的解决了代码紧耦合(tight couple)的问题,是一种让代码实现松耦合(loose couple)的机制。

3、松耦合让代码更具灵活性,能更好地应对需求变动,以及方便单元测试。

依赖注入的缺点:

使用依赖注入时,往往会配合反射使用,这在一定程度上影响程序的性能。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值