笔者通常希望应用程序中的组件尽可能独立,而只有很少几个可控的依赖项。—— 在理想情况下,每个组件都不了解其他组件,而只是通过抽象接口来处理应用程序的其他区域。这称为松耦合 。—— 它能够使应用程序更易于测试和修改。

  当我们需要编写或者引用一个组件来实现一系列功能时(比如编写一个名称为“MyEmailSender”的组件,用以发送邮件信息),我们可以定义一个接口,它包含了发送邮件所需的所有 public 函数(这个接口可以命名为 “IEmailSender”)。

  (IEmailSender 是一个接口,MyEmailSender 是该接口的一个具体实现类)

  应用程序中任何需要发送电子邮件的部分(即“应用场景”,比如名称为 “PasswordResetHelper” 的“密码重置辅助程序”),只要通过引用该接口中的方法便可以发送一份邮件

  通过引入 IEmailSender,保证了 PasswordResetHelper 与 MyEmailSender 之间没有直接的依赖项。

  这样的好处是,笔者完全可以用另一个邮件发送程序来替换 MyEmailSender,甚至用一个模仿实现以进行测试,而无须对 PasswordResetHelper 做任何修改。

3.3.1 使用依赖项注入

   接口有助于解除组件耦合,但这里仍面临一个问题:

   PasswordResetHelper 类要通过 IEmailSender 接口来配置并发送电子邮件,总归需要创建一个实现该接口的对象(必须创建一个 MyEmailSender 的实例)。但 C# 并未提供内置的方法以方便地创建实现接口的对象,除非以 new 关键字创建一个具体组件的实例。于是,代码如下:

    public class PasswordResetHelper
    {
      public void ResetPassword()
      {
        IEmailSender mySender = new MyEmailSender();
        //调用接口方法,以配置 email 细节
        mySender.SendEmail();
      }
    }

    这破坏了无须修改 PasswordResetHelper 就能替换 MyEmailSender 的目的,这意味着此刻仅处于组件松耦合的半途。

    现在需要有一种办法,它能够获取实现某接口的对象,而不必直接创建该对象。—— 这一问题的解决方法称为依赖项注入(DI),也称为控制反转(IoC)

    DI 是一种实现组件解耦的设计模式。(也是有效从事 MVC 开发的一个重要概念。同时,DI 也可能会造成很多困惑)

DI 模式有两个部分:

 1. 打断和声明依赖项

    第一个部分是从组件(此例中的 PasswordResetHelper)中去除掉对具体类的依赖项。其做法是创建一个以所需接口的实现作为其参数的类构造器(构造方法)。如下所示:

      public class PasswordResetHelper
      {
        private IEmailSender emailSender ;         public PasswordResetHelper (IEmailSender emailSenderParam)
        {
          emailSender = emailSenderParam ;
        }         public void ResetPassword ()
        {
          emailSender.SendEmail();
        }
      }

    现在可以将 PasswordResetHelper 类的构造器(构造方法)称为对 IEmailSender 接口声明了一个依赖项

   (除非接受一个实现了 IEmailSender 接口的对象,否则便不能创建和使用它 —— PasswordResetHelper 类

    在依赖项声明中,PasswordResetHelper 类不再有 MyEmailSender 的任何知识,它仅仅依赖于 IEmailSender 接口。简言之,PasswordResetHelper 不再了解或关心如何实现 IEmailSender 接口

 2. 注射依赖项

    DI 模式的第二个部分在创建 PasswordResetHelper 类的实例时注入由其声明的依赖项。(即,在调用 PasswordResetHelper 的构造方法创建 PasswordResetHelper 类的实例时,把依赖项 —— MyEmailSender 的实例放进 PasswordResetHelper 的构造方法的参数里面去。故称为依赖项注入

   (其实这也很好理解:第一步你要声明说这里需要放一个什么,那么第二步当然就是把它放进去啦!)—— 这种依赖项是在运行时处理的。

    上述 PasswordResetHelper 类是通过构造器(构造方法/构造函数)来声明其依赖项的,这称为 “构造器注入”。

     也可以通过一个 public 属性来声明要注入的依赖项,这称为 “设置器注入”,意即在该属性的 set 代码块中声明依赖项

3.2.2 使用依赖项注入容器

    至此已解决了依赖项问题,但按照现在的情况,在应用程序的某个地方仍然需要以下这些语句。

      IEmailSender sender = new MyEmailSender();
      helper = new PasswordResetHelper(sender);

    如何在无须在应用程序的某个其他地方创建依赖项而对接口的具体实现进行实例化呢? ——  答案是使用 “依赖项注入容器(简称 DI 容器)”

    DI 容器是一种组件,它在类所声明的依赖项和用来解决这些依赖项的类( PasswordResetHelper 和 MyEmailSender之间充当着中间件的角色

    可以用这种 DI 容器注册一组应用程序要使用的接口或抽象类型,并指明满足依赖项所需实例化的实现类。

    因此在上例中,便会用 DI 容器注册 IEmailSender 接口并指明在需要实例化 IEmailSender 时,应该创建一个 MyEmailSender 的实例

   (这里讲的就是第二步 —— 依赖项注入所以重点在 IEmailSender 和 MyEmailSender

    当应用程序中需要一个 PasswordResetHelper 对象时,便要求 DI 容器去创建一个

    DI 容器知道 PasswordResetHelper 已经声明了一个关于 IEmailSender 接口的依赖项,而且知道已经将 MyEmailSender 类指定为用于该接口的实现。

    DI 容器会将这两项信息结合在一起从而创建 MyEmailSender 对象,然后用它作为创建 PasswordResetHelper 对象的一个参数于是在应用程序中便可以使用这个 MyEmailSender 了

    重要的是要注意到:应用程序中已经不需要自己动手使用 new 关键字去创建这种对象了而是进入 DI 容器请求所需的对象

    (DI 新手可能还需要一段时间去适应,但后面会看到,MVC 框架提供了一些特性,可以使该过程更加简单

    我们不需要自己去编写 DI 容器有一些很棒的开源代码和免费的许可实现是可用的

    笔者所喜欢的并在自己的项目中使用的叫做 Ninject,可以在 http://www.ninject.org 上获得其细节。(第 6 章将介绍 Ninject 的使用,并演示如何用 NuGet 安装这个包

    微软公司创建了自己的 DI容器,叫做 Unity .(如果需要了解更多关于 Unity 的信息,可参阅 unity.codeplex.com)

    DI 容器的作用似乎简单而平常,但事实并非如此。—— 一个好的 DI 容器,如 Ninject,有一些聪明的特性

       1、依赖链解析:

          如果 MyEmailSender 类的构造器需要一个 INetworkTransport 接口的实现,DI 容器将实例化这个接口的默认实现,把它传递给 MyEmailSender 的构造器,并将返回结果作为 IEmailSender         的默认实现。)

       2、对象生命周期管理:

          一个好的 DI 容器能让你配置组件的生命周期允许你从预定义的选项中进行选择。这些选项包括……(但是这里并没有介绍为什么要进行这个设置)

       3、构造器参数值的配置:

          如果 INetworkTransport  接口实现的构造器需要一个叫做 serverName 的字符串,你应该能够在 DI 容器的配置中为其设置一个值。

          这是一种笨拙但简单的配置系统,它不需要你的代码传递诸如连接字符串、服务器地址等参数。

    最后,编写自己的 DI 容器是理解 C# 和 .NET 如何处理类型及反射的一种很好的方式,而且建议将其作为闲暇而无事可做时的一个好项目。

    但切不要试图在一个实际的项目中部署你写的这些代码。—— 编写可靠、健壮且高性能的 DI 容器是困难的,你应该找一个已经过验证且测试过的包来使用。

3.3 建立松耦合组件(MVC 模式最重要的特性之一是它支持、关注“分离”)《精通 ASP.NET MVC 5》 推荐指数:8 星半的更多相关文章

  1. Prism 4 文档 ---第9章 松耦合组件之间通信

    当构建一个大而负责的应用程序时,通用的做法时将功能拆分到离散的模块程序集中.将模块之间的静态引用最小化.这使得模块可以被独立的开发,测试,部署和升级,以及它迫使松散耦合的沟通. 当在模块之间通信时,你 ...

  2. 3.4 自动测试初步《精通ASP.NET MVC 5》

    概述 ASP.NET MVC 框架已被设计成易于建立自动测试,并易于采用诸如测试驱动开发(TDD)等的开发方法学.ASP.NET MVC 为自动化测试提供了一个理想平台. 从广义上讲,当今的 Web ...

  3. 【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目 目录索引

    索引 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目(1)搭建MVC环境 注册区域 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目(2)创建 ...

  4. 精通ASP.Net MVC 3 框架(第三版)学习笔记

    精通ASP.Net MVC 3 框架(第三版)学习笔记 代码才是王道. http://pan.baidu.com/s/1pJyL1cn

  5. 第 4 章—— C# 语言特性(《精通 ASP.NET MVC 5》)

    这里只提供各个特性的简单概括. C# 的完整指南可参阅<Introducing Visual C#>.深度了解 LINQ 可参考<Pro LINQ in C#> 4.1 准备示 ...

  6. 精通 ASP.NET MVC 4 学习笔记(一)

    这里记录着从 P132 到 P192 的内容.水分很足,大部分是书上的代码,我只加了一些基于我自己的理解的能帮助初学者看懂的注释,并且把书中的部分内容做了一些的拓展. 建立数据层 设置 DI 容器 / ...

  7. 【开源分享:入门到精通ASP.NET MVC+EF6+Bootstrap】从这里开始,一起搭框架(1)开篇介绍

    框架简介 这几年一直在做ASP.NET开发,几年前做项目都是老老实实一行行的写代码,后来发现那些高手基本都会有自己积累起来的代码库,现在称之为开发框架,基础代码不用再去堆,主要精力可以集中在业务逻辑实 ...

  8. 使用整体模型模板辅助器 Using Whole-Model Templated Helpers 模板辅助器方法 精通ASP.NET MVC 5

    怎么会

  9. Creating Form Elements --Using BeginForm and EndForm 使用内建的Form辅助器方法 精通ASP.NET MVC 5

    Using the BeginForm and EndForm Helper Methods in the CreatePerson.cshtml File

随机推荐

  1. 【读书笔记】SpringBoot读书笔记

    整体目录结构: 一.入门 二.开发第一个应用程序 三.自定义配置 四.测试 五.Groovy与Spring Boot Cli 六.在Spring Boot中使用Grails 七.深入Actuator ...

  2. Unity3D关于VR的Demo(一)

    https://blog.csdn.net/qq_15807167/article/details/52048998?locationNum=8&fps=1 阅读数:9716 最近有点忙,只有 ...

  3. PHP实现多进程并行操作,可做守护进程(转,备用)

    <?php /** * 入口函数 * 将此文件保存为 ProcessOpera.php * 在terminal中运行 /usr/local/php/bin/php ProcessOpera.ph ...

  4. html 5实用特性之data属性

    HTML 5之前,我们必须依赖于class和rel属性来存储需要在网站中使用的数据片段,这种做法有时会在网站的外观和实用性之间产生冲突.而HTML 5 Data属性的存在就能很好满足需要. HTML5 ...

  5. 20145122 《Java程序设计》第十周学习总结

    学习内容总结 网络编程 (1)网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据. (2)程序员所作的事情就是把数据发送到指定的位置,或者接收到指定的数据,这个就是狭义的网络编程范畴. (3 ...

  6. C#中值和引用

    c#中有两种基本类型,它们分别是值类型和引用类型:而每种类型都可以细分为如下类型: ps:1.基本类型是值类型 2.类.接口.委托都是引用类型

  7. 三羊献瑞|2015年蓝桥杯B组题解析第三题-fishers

    三羊献瑞 观察下面的加法算式: 祥 瑞 生 辉 三 羊 献 瑞 三 羊 生 瑞 气 (如果有对齐问题,可以参看[图1.jpg]) 其中,相同的汉字代表相同的数字,不同的汉字代表不同的数字. 请你填写& ...

  8. 用 C# 计算 与 java 一致的unix时间戳 (长时间整形 如:1476956079961)

    背景: 调用java提供接口,需要长时间整形作为验证. 预备知识: 1. java 的 System.currentTimeMillis() 计算的长整型,是从1970年1月1日开始,截止当前的毫秒数 ...

  9. 关于ArrayList.clear()与=null以及new ArrayList<E>()

    ArrayList是常用到的JCF类,用来保存类型相同的一组对象,并通过下标来快速访问指定对象.今天讨论的是当我们使用完ArrayList后应该选择怎样合适的处理方式. 这里现在有三种方式如下: 1. ...

  10. Win10重命名文件夹导致资源管理器卡顿的解决办法

    我本机使用的是 Win10 1607,不清楚是因为什么原因导致重命名文件夹时资源管理器会被卡死,找了很长时间终于找到了解决办法,现在我把步骤粘出来以便后续遇到相同问题的朋友能及时解决. 其实操作很简单 ...