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

  当我们需要编写或者引用一个组件来实现一系列功能时(比如编写一个名称为“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. P3627 [APIO2009]抢掠计划

    P3627 [APIO2009]抢掠计划 Tarjan缩点+最短(最长)路 显然的缩点...... 在缩点时,顺便维护每个强连通分量的总权值 缩完点按照惯例建个新图 然后跑一遍spfa最长路,枚举每个 ...

  2. finedb(内置的HSQL数据库)迁移数据到MySQL

    finedb(内置的HSQL数据库)迁移数据到MySQL 1. 前言 在FineBI中,决策平台的数据(用户.角色.组织机构.权限等信息)是存储在finedb数据库中的,默认情况下finedb是一个内 ...

  3. C++设计模式 之 “单一职责”模式:Decorator、Bridge

    part 1 “单一职责”模式 在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任. 典型模式 Decorato ...

  4. 使用liner、feather、multiband对已经拼接的数据进行融合

    所谓"blend",英文解释为“vt. 混合vi. 混合:协调n. 混合:掺合物”这里应该理解为是图像数据的融合.这是“识别->对准->融合”的最后一步.融合是决定拼接 ...

  5. Codeforces 788A Functions again - 贪心

    Something happened in Uzhlyandia again... There are riots on the streets... Famous Uzhlyandian super ...

  6. Python3基础 list str转成list

             Python : 3.7.0          OS : Ubuntu 18.04.1 LTS         IDE : PyCharm 2018.2.4       Conda ...

  7. Linux内存管理--用户空间和内核空间【转】

    本文转载自:http://blog.csdn.net/yusiguyuan/article/details/12045255 关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用 ...

  8. JavaScript:Object属性方法

    Object的属性(firebug中没有找到) var pro={ city:"shanghai", list:[,,,,] } var Person=function(name, ...

  9. [SpringBoot] - 上线一份项目记录

    首先在服务器上运行war包. (新建项目) 其后,选择数据库,因为之前感觉mysql比较难安装,这次就再试一次,之前的PostgreSQL没有问题. 将原有文件进行复制,排除导包错误. 首先测试邮件发 ...

  10. 接口中带参方法,传入IB类型的数据

    不同的接口有不同的方法 不同的类有不同的作用 不同的作用产生不一样的效果 不同的效果让程序看似复杂,实际简单... 比如此程序,看似复杂,实际就那么点事: 谁生成了谁,谁设置了谁,谁传入了谁,谁被谁调 ...