Dependency Injection 筆記 (3)
续上集。接着要来进一步了解的是 DI 的实现技术,也就是注入相依对象的方式。这里介绍的依赖注入方式,又称为「穷人的 DI」(poor man’s DI),因为这些用法都与特定 DI 工具无关,亦即不使用任何现成的 DI 框架(例如 Unity、Autofac)。毕竟,DI 只是一组设计原则与模式,不依赖任何工具也能实现。
(本文摘自電子書:《.NET 依賴注入》)
设计模式梗概
每个模式都描述了一个不断发生在我们周遭的问题,然后描述该问题的核心解法,于是你便可以一再使用该解法,而无须对同样的事情做两次工。
—— Christopher Alexander. A Pattern Language.
除了第 1 章提到的 S.O.L.I.D. 设计原则,在运用 DI 技术时,也经常需要搭配一些设计模式(design patterns),例如 Factory Method(工厂方法)、Decorator(装饰)、Composite(组合)、Adapter(转换器)等等。基于后续章节讨论的必要,本节将介绍几个相关的设计模式。如需比较完整深入的介绍,可参考相关书籍,例如:《面向对象设计模式》、《深入浅出设计模式》、《重构-向范式前进》等等。
小引-电器与接口
日常生活中,四处可见电器用品,例如电视、微波炉、计算机等等。这些电器通常都有条电线,电线尾端是个插头,而当我们要使用这些电器时,就把插头插在墙壁或电源插座上,电器便能够获得所需之电力。一般情况下,没有人会舍插座不用,而把电器的电源线固定焊在墙壁的电源插座。假使真这么做,万一有一天电视或计算机故障而需要维修,那可就麻烦了。

不只电源插座,计算机的 USB 插槽也一样——它们都具备宽松耦合的特性。这里的电源插座或 USB 插槽,对应到软件世界里的概念,便是接口。一个接口就等于是一份规格,而各家厂商所生产的各式各样的电源插座或 USB 插槽,就是遵照其标准规格(接口)所实现出来的产品,或简称实现品。用软件的术语来说,这些实现品就是类型——实现了特定接口的类型。
接口的威力即在于一旦订出标准规格,各家厂商便可依照标准接口来制作各类产品。对使用者来说,好处则是享有多种选择,因为他们不会被特定厂商的产品绑住;只要他们高兴,随时可以更换不同的产品,而且通常是即插即用。在软件的世界里,接口也有同样的好处:让类型与类型之间保持宽松耦合,以便提供随时抽换实现类型的弹性。
Null Object 模式
回到电源插座的例子。如果我们将计算机的电源线从插座上拔起,它们就只是彼此不再连接而已,计算机和插座并不会因此而着火或爆炸。但是在软件程序的世界里,若对象 A 会调用对象 B(对象 A 依赖对象 B),而当你将对象 B 移除,亦即对象 B 不存在时,程序就会发生 NullReferenceException 类型的错误。于是,我们常常会在程序里面加入检查对象参考是否为 null 的逻辑,例如:
if (anObject != null)
anObject.DoSomething();
else
DoSomethingElse();
如果在程序中一再重复写这些检查 null 的逻辑,代码便会膨胀,而且在解读程序的主要逻辑时,常常得要跳过这些检查逻辑,多少会形成阅读代码的阻碍。针对此问题,我们可以设计一个空的、完全不做任何事的类型,然后在变量有可能是 null 的地方,让它们指向那个空的对象。这种模式叫做 Null Object。
Null Object 的优点:可减少编写判断对象参考是否为 null 的防错逻辑。但前提是开发人员得知道有 Null Object 可用,否则还是会写出多余的防错代码。
Null Object 类型通常要实现某个接口(或继承自抽象类型),但实现代码完全没做任何事,即所有方法都只是个空壳子,或仅提供无害的默认行为。以程序中常用的 logging(日志)机制为例,我们可以将写入日志的操作定义成一个 ILogger 接口,然后依实际需要实现不同的 logging 类型,例如用来将日志讯息输出至 Console 的 ConsoleLogger。此外,考虑到应用程序有时候可能不需要纪录任何讯息,我们可以实现一个 NullLogger 类型,当作 Null Object 使用。结构图如下。

底下分别是 ILoger 接口以及 NullLogger 和 ConsoleLogger 类型的代码:
public interface ILogger
{
Log(string msg);
} public class NullLogger : ILogger
{
public void Log(string msg)
{
// 不做任何事
}
} public class ConsoleLogger : ILogger
{
public void Log(string msg)
{
Console.WriteLine(msg);
}
}
像底下这个函式,调用端只要传入 ConsoleLogger 对象,日志讯息就会输出至 Console;而当调用端想要停止记录日志,便可传入 NullLogger 对象。如此一来,就不用在每次写入日志讯息时都重复写一遍检查 logger 对象是否为 null 的防错逻辑。
void DoSomething(ILogger logger)
{ logger.Log("开始执行 DoSomething 函式。");
....
}
Note: Null Object 本身并不需要「进化」成真正有做事的对象,因为它的存在就是为了提供一个完全不做任何事、不具任何意义的对象。
Decorator 模式

延续前面的 logging 范例,假设想要在每次输出 log 讯息时额外加上当时的日期时间,而且前提是不可修改现有的 ILogger 和 ConsoleLogger 类型,该怎么做?
我们可以使用 Decorator 模式。作法为:设计一个新的类型,此类型不仅要实现 ILogger 接口,而且还需要使用现有的 ConsoleLogger 对象来输出 log 讯息。简单起见,我就把这个类型命名为 DecoratedLogger。代码如下:
public class DecoratedLogger : ILogger
{
private ILogger logger; public DecoratedLogger(ILogger aLogger)
{
logger = aLogger;
} public void Log(string msg)
{
logger.Log(DateTime.Now.ToString() + " - " + msg);
}
}

void DoSomething()
{
ILogger logger = new DecoratedLogger(new ConsoleLogger());
logger.Log("Hello, 裝飾模式!");
}
Dependency Injection 筆記 (3)的更多相关文章
- Dependency Injection 筆記 (2)
续上集,接着要说明如何运用 DI 来让刚才的范例程序具备执行时期切换实现类型的能力. (本文摘自電子書<.NET 依賴注入>) 入门范例—DI 版本 为了让 AuthenticationS ...
- Dependency Injection 筆記 (1)
<.NET 依賴注入>連載 (1) 本文从一个基本的问题开始,点出软件需求变动的常态,以说明为什么我们需要学习「依赖注入」(dependency injection:简称 DI)来改善设计 ...
- Dependency Injection 筆記 (4)
续上集未完的相关设计模式... (本文摘自電子書:<.NET 依賴注入> Composite 模式 延续先前的电器比喻.现在,如果希望 UPS 不只接计算机,还要接电风扇.除湿机,可是 U ...
- Dependency Injection
Inversion of Control - Dependency Injection - Dependency Lookup loose coupling/maintainability/ late ...
- 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 ...
随机推荐
- sparksql 动态设置schema将rdd转换成dataset/dataframe
java public class DynamicDemo { private static SparkConf conf = new SparkConf().setAppName("dyn ...
- Methods for Using Message Queuing Telemetry Transport for Sensor Networks to Support Sleeping Devices
Methods support a sleep mode for an embedded device. Embedded devices like sensors and actuators use ...
- R 语言文件读写
1. working directory:工作目录 > getwd() > setwd("C:/data") # 设定当前工作目录 2. 读取格式化的 table &g ...
- yii2 实现无极限分类
在商城项目或者其他的项目中无极限分类是非常常见的场景 那么许多人都会这样干 利用递归每次查询数据库,说来惭愧,我以前也是这样干的 总有点误人子弟的感觉 这样做在数据量大的情况下会有一定的延迟 publ ...
- 探究Promise的实现
最终答案在一个类库里,地址 https://github.com/yahoo/ypromise 这个类库也有问题,就是下面这道面试题在IE9里实现不一致,类库里还是用了setTimeout.去年尝试用 ...
- webpack打包不引入vue、echarts等公共库
如果我们打包的时候不想将vue.echarts等公共库包含在内,需要配置两处地方, 以下以基于vue-cli生成的项目为基准: 1webpack配置: // webpack.base.conf.js ...
- Linux input
Linux input 输入设备都有共性:中断驱动+字符IO,基于分层的思想,Linux内核将这些设备的公有的部分提取出来,基于cdev提供接口,设计了输入子系统,所有使用输入子系统构建的设备都使用主 ...
- [Example of Sklearn] - 分类对比
refrence :http://cloga.info/python/2014/02/07/classify_use_Sklearn/ 加载数据集 这里我使用pandas来加载数据集,数据集采用kag ...
- 我眼中的robot framework
由于近期公司需要,需要一个测试框架对于公司的服务做自动化测试. 由于服务的复杂性,人工测试的方式越来越复杂,体现在以下方面: 1.人工测试步骤复杂,容易出错.服务的复杂性会使人工测试的准备工作,测试条 ...
- Android TV开发中所有的遥控器按键监听及注意事项,新增home键监听
原文:Android TV开发中所有的遥控器按键监听及注意事项,新增home键监听 简单记录下android 盒子开发遥控器的监听 ,希望能帮到新入门的朋友们 不多说,直接贴代码 public cla ...