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、松耦合让代码更具灵活性,能更好地应对需求变动,以及方便单元测试。

依赖注入的缺点:

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

  • 15
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
C#中,依赖注入是一种设计模式,用于将一个对象的依赖关系从它的客户端解耦。通过依赖注入,创建对象时,它所依赖的其他对象会被注入到它的构造函数、属性或方法中,从而实现对象之间的松耦合。 在C#中,可以使用构造方法注入和属性注入两种方式来实现依赖注入。 构造方法注入是在对象的构造方法中将依赖对象作为参数传入,例如: ```csharp internal class ClientClass { private IServiceClass _serviceImpl; public ClientClass(IServiceClass serviceImpl) { this._serviceImpl = serviceImpl; } // ... } ``` 属性注入是通过对象的属性来注入依赖对象,例如: ```csharp internal class ClientClass { public IServiceClass ServiceImpl { get; set; } // ... } ``` 需要注意的是,构造方法注入只能在实例化客户类时注入一次,而属性注入可以在程序运行期间改变客户类对象内的服务类实例。 为了实现依赖注入,通常需要使用一个依赖注入容器(例如:Microsoft.Extensions.DependencyInjection),通过容器来管理依赖对象的创建和注入。 除了依赖注入容器,还可以使用配置文件来管理依赖对象的创建和注入。可以使用ConfigurationBuilder类来导入配置文件,并使用IOptions<T>来获取配置文件中的内容。 总结起来,C#中的依赖注入是通过构造方法注入或属性注入来创建对象并将依赖对象注入其中。使用依赖注入容器可以更方便地管理依赖对象的创建和注入,而配置文件可以用于配置和管理依赖对象的属性值。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [C#依赖注入](https://blog.csdn.net/qq_18145031/article/details/83181937)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [c#依赖注入](https://blog.csdn.net/wanxiweilai/article/details/127878160)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值