Dependency Injection
Inversion of Control
- Dependency Injection
- Dependency Lookup
loose coupling/maintainability/
late binding
abstract factory
unit test
container
非 DI 的版本
底下這段程式碼是 Console 應用程式的進入點:
class Program
{
static void Main(string[] args)
{
HeavyDuty aTask = new HeavyDuty();
aTask.Run();
}
}
Main 函式會先建立類別 HeavyDuty 的執行個體,然後呼叫該物件的 Run 方法。HeavyDuty 類別的原始碼如下:
public class HeavyDuty
{
private ConsoleLogger logger; public HeavyDuty()
{
// 在建構式裡面就先建立好欲使用的記錄器.
logger = new ConsoleLogger();
} public void Run()
{
logger.WriteEntry("HeavyDuty is running...");
}
}
從 HeavyDuty 類別的原始碼可以發現,它使用了另一個叫做 ConsoleLogger 的類別來當作記錄器,以便輸出一些訊息。ConsoleLogger 的責任很簡單,就只是將指定的訊息輸出至 console 視窗而已:
public class ConsoleLogger
{
public void WriteEntry(string msg)
{
Console.WriteLine(msg);
}
}
整理一下:主程式會用到 HeavyDuty 類別來執行某項工作,而 HeavyDuty 又會使用 ConsoleLogger 來輸出 log 訊息。三者關係如下:

這個例子的情境是:應用程式常常會需要寫 log,而寫 log 的機制有好多種,例如本例的 ConsoleLogger 是將 log 訊息輸出至 console 視窗,其他可能的 log 方式還有:寫入 Windows 事件日誌、發送 e-mail、寫入資料庫等等。
問題來了,此例的 ConsoleLogger 是由 HeavyDuty 類別所建立,並非由主程式控制,如果應用程式中還有其他類別需要寫 log,也就必須像 HeaveyDuty 那樣,在類別裡面建立 ConsoleLogger 的物件實體並呼叫其方法。如此一來,若有 N 個類別要寫 log,就有 N 個類別相依於 ConsoleLogger 類別。萬一有一天要改成寫入 Windows 事件日誌(可能會設計另一個 WindowsEventLogger 類別),這要改多少程式碼呀?
接著就來看看 DI 如何處理這個問題。
改成 DI 版本
看過了非 DI 版本的程式寫法以及類別圖,我們知道未來可能會有很多類別會相依於 ConsoleLogger 這個具象類別(concrete class),而這層相依性,極可能造成日後很高的維護成本。因此,我們的首要目標就是減輕、甚至消除這層相依性,或者說:解耦合(decouple)。
前兩篇曾提過,介面是解耦合的一種很好用的工具。故我們可以先把「寫入 log」這個操作放到一個介面中,讓所有要寫 log 的類別只針對一個標準介面來操作。就將此介面命名為 ILogger 好了。程式碼很簡單,就只有一個方法:
public interface ILogger
{
void WriteEntry(string msg);
}
然後,原本的 ConsoleLogger 類別(以及其他要提供寫 log 操作的類別)必須實作此介面:
public class ConsoleLogger : ILogger
{
public void WriteEntry(string msg)
{
Console.WriteLine(msg);
}
}
接下來,我們希望 HeavyDuty 類別(以及未來其他需要寫 log 的類別)只依賴 ILogger 介面,而不要依賴特定實作。因此,原先在 HeavyDuty 中建立 ConsoleLogger 物件實體的寫法就必須拿掉。可是,要使用物件之前,一定得在某個地方先建立好物件的實體才行啊。那麼,要在哪裡、由誰來建立物件呢?
建立 logger 物件的工作,由於需要用到具象類別,此動作會產生相依性。我們希望將依賴程度盡量降低,因此我們可以將此相依性從 HeavyDuty 類別中抽離,轉移至主程式。換言之,由主程式來建立真正的 logger 物件實體。那麼,HeavyDuty 只要有一個指向實際 logger 物件的參考就夠了--這很簡單,只要主程式在建立 HeavyDuty 物件時,透過建構式的參數傳入 logger 物件參考就解決了。
所以修改後的 HeavyDuty 類別會像這樣:
public class HeavyDuty
{
private ILogger logger; public HeavyDuty(ILogger aLogger)
{
logger = aLogger;
} public void Run()
{
logger.WriteEntry("HeavyDuty is running...");
}
}
有注意到嗎?類別裡面完全沒有指涉任何 logger 類別,而只用到 ILogger 介面而已。真正的物件實體,是透過建構式的參數傳進來。這種透過建構式來提供(注入)相依物件的作法,叫做「建構式注入」(Constructor Injection)。
最後是主程式的 Main 方法:
class Program
{
static void Main(string[] args)
{
ILogger logger = new ConsoleLogger();
HeavyDuty aTask = new HeavyDuty(logger);
aTask.Run();
}
}
到這裡應該完全清楚了。若還覺得有點模糊,可試著倒著順序往回逐一檢視程式碼,應該也能理出一些頭緒。了解各類別之間的關係之後,也就不難整理出底下的類別圖了。

你也可以從這張類別圖,搭配程式碼來推敲各類別的關聯。同時想想看為甚麼要這樣設計,這樣設計有什麼好處(前面都有提到)。
Dependency Injection的更多相关文章
- Ninject学习(一) - Dependency Injection By Hand
大体上是把官网上的翻译下而已. http://www.ninject.90iogjkdcrorg/wiki.html Dependency Injection By Hand So what's Ni ...
- MVC Controller Dependency Injection for Beginners【翻译】
在codeproject看到一篇文章,群里的一个朋友要帮忙我翻译一下顺便贴出来,这篇文章适合新手,也算是对MEF的一个简单用法的介绍. Introduction In a simple stateme ...
- 控制反转Inversion of Control (IoC) 与 依赖注入Dependency Injection (DI)
控制反转和依赖注入 控制反转和依赖注入是两个密不可分的方法用来分离你应用程序中的依赖性.控制反转Inversion of Control (IoC) 意味着一个对象不会新创建一个对象并依赖着它来完成工 ...
- [转载][翻译] IoC 容器和 Dependency Injection 模式
原文地址:Inversion of Control Containers and the Dependency Injection pattern 中文翻译版本是网上的PDF文档,发布在这里仅为方便查 ...
- Inversion of Control Containers and the Dependency Injection pattern(转)
In the Java community there's been a rush of lightweight containers that help to assemble components ...
- Scala 深入浅出实战经典 第57讲:Scala中Dependency Injection实战详解
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- 【译】Dependency Injection with Autofac
先说下为什么翻译这篇文章,既定的方向是架构,然后为了学习架构就去学习一些架构模式.设计思想. 突然有一天发现依赖注入这种技能.为了使得架构可测试.易维护.可扩展,需要架构设计为松耦合类型,简单的说也就 ...
- 依赖注入 | Dependency Injection
原文链接: Angular Dependency Injection翻译人员: 铁锚翻译时间: 2014年02月10日说明: 译者认为,本文中所有名词性的"依赖" 都可以理解为 & ...
- 黄聪:Microsoft Enterprise Library 5.0 系列教程(八) Unity Dependency Injection and Interception
原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(八) Unity Dependency Injection and Interception 依赖注入容器Uni ...
随机推荐
- zTree v3.5配置
页面 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ZTree3.aspx ...
- python调用系统命令popen、system
python调用Shell脚本,有两种方法:os.system(cmd)或os.popen(cmd),前者返回值是脚本的退出状态码,后者的返回值是脚本执行过程中的输出内容.所以说一般我们认为popen ...
- HR外包系统 - 客户公司薪资规则 报表需求 记入系统
1 薪酬规则,包括 常用薪资项目 2 报表需求,特别是报表排序规则 3 特殊项说明记录 另外包括客户公司监控的日期设置
- Hadoop RPC机制的使用
一.RPC基础概念 1.1 RPC的基础概念 RPC,即Remote Procdure Call,中文名:远程过程调用: (1)它允许一台计算机程序远程调用另外一台计算机的子程序,而不用去关心底层的网 ...
- zookeeper源码分析(一) 工作原理
来自:http://www.codedump.info/?p=207 阅读zookeeper代码一段时间(注:是很长一段时间,断断续续得有半年了吧?)之后,我要开始将一些积累下来的东西写下来了,鉴于我 ...
- Jmeter 检查点
Jmeter的检查点就是插入个断言,但用下来不好用,没LR好用,先放放.
- LoadRunner常用函数列表
LoadRunner常用函数列表 Web相关函数 函 数 功 能 描 述 web_custom_request 用户可以通过该函数自行创建一个HTTP请求的函数 web_image 模拟用户单击 ...
- 你必须知道的.NET之特性和属性(转)
1. 引言 attribute是.NET框架引入的有一技术亮点,因此我们有必要花点时间走进一个发现attribute登堂入室的入口.因为.NET Framework中使用了大量的定制特性来完成代码约定 ...
- Android拓展系列(12)--使用Gradle发布aar项目到JCenter仓库
目的 发布自己的android library(也就是aar)到公共的jcenter仓库,所有的人都能用gradle最简单的方式引用. 为什么选择jcenter,它兼容maven,而且支持更多形式仓库 ...
- 《DSP using MATLAB》示例Example4.4
代码: x1 = [2, 3, 4]; x2 = [3, 4, 5, 6]; % x1 x2 sequences % n1 = 0:1:2; n2 = 0:1:3; n1 = 0:1:length(x ...