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

  当我们需要编写或者引用一个组件来实现一系列功能时(比如编写一个名称为“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. wireshark捕获表达式之Berkeley Packet Filter (BPF) syntax

    就网络抓包来说,绝大部分的情况下,我们都是对特定的ip/端口/协议进行捕获和分析,否则就会有大量的垃圾报文,使得分析和性能低下.大部分的抓包工具都采用BPF语法,具体可参考 http://biot.c ...

  2. EF 一个实体对象不能由多个 IEntityChangeTracker 实例引用 解决办法

    在DAL层中,建立工厂类 namespace DAL { public static class SysDbContextFactory { /// <summary> /// 从Http ...

  3. 01: 重写Django admin

    目录: 1.1 重写Django admin项目各文件作用# 1.2 重写Django admin用户认证 1.3 将要显示的表注册到我们自己的kind_admin.py中 1.4 项目首页:显示注册 ...

  4. python监控端口脚本[jkport2.0.py]

    #!/usr/bin/env python #!coding=utf-8 import os import time import sys import smtplib from email.mime ...

  5. 20145225 《网络对抗》逆向及Bof基础实践

    实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串. 该程序同时包含另一个代码片段,getShe ...

  6. 面向对象第一话,大战java正则表达式

    本周我们迎来第一项任务,java面向对象之实现对一串字符的匹配以及构造出计算的多项式,最终得出计算的结果.简而言之,可以用以下的要求来看题目: 输入的多项式字符串中不得出现非法字符 多项式的输入型式, ...

  7. numpy.random.randn()与rand()的区别【转】

    本文转载自:https://blog.csdn.net/u010758410/article/details/71799142 numpy中有一些常用的用来产生随机数的函数,randn()和rand( ...

  8. ACM-ICPC 2018 徐州赛区网络预赛H Ryuji doesn't want to study(树状数组)题解

    题意:给你数组a,有两个操作 1 l r,计算l到r的答案:a[l]×L+a[l+1]×(L−1)+⋯+a[r−1]×2+a[r] (L is the length of [ l, r ] that ...

  9. newcode wyh的吃鸡(优势队列+BFS)题解

    思路: 要用优势队列,因为有的+2,有的+1,所以队列中的步长是不单调的,所以找到一个答案但不一定最小,所以用优势队列把小的放在队首. 要记录状态,所以开了三维,题目和昨天做的那道小明差不多 vis开 ...

  10. C#中的异步编程模式

    异步编程模型(APM) 基于事件的异步编程模式 基于任务的异步模式 Async Await编程 关于C#,可以看看Learning Hard的博客