上集。接着要来进一步了解的是 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 模式

一般情况下,如果在使用计算机时突然停电了,尚未储存的数据就会消失不见。为了解决此问题,我们可以在墙壁的电源插座与计算机电源线之间加入一个不断电系统(UPS)。此时,UPS 的电源线会接在墙壁的电源插座上,而计算机的电源则改接在 UPS 上。此三者在串接的时候,都是通过单一的标准接口:插座。类似这种通过同一接口来串接多个不同对象的作法,叫做Decorator Pattern(装饰模式)。此模式可以让我们为对象层层迭加新的功能上去,而无须修改现有的类型。下图为 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);
}
}
下图描绘了这个简略版本的 Decorator 模式范例的类型结构:
 
 
于是,在客户端程序中使用这个新的 DecoratedLogger 来输出 log 讯息时,可以这么写:
 
    void DoSomething()
{
ILogger logger = new DecoratedLogger(new ConsoleLogger());
logger.Log("Hello, 裝飾模式!");
}
你可以看到,在这次的修改当中,现有的 ILogger 和 ConsoleLogger 完全没有动到。我们只增加了一个新类型(DecoratedLogger),就为应用程序加上了新功能。这也就符合了第 1 章提过的 OCP(开放/封闭原则)。
 
(摘自:《.NET 依賴注入》)
 

Dependency Injection 筆記 (3)的更多相关文章

  1. Dependency Injection 筆記 (2)

    续上集,接着要说明如何运用 DI 来让刚才的范例程序具备执行时期切换实现类型的能力. (本文摘自電子書<.NET 依賴注入>) 入门范例—DI 版本 为了让 AuthenticationS ...

  2. Dependency Injection 筆記 (1)

    <.NET 依賴注入>連載 (1) 本文从一个基本的问题开始,点出软件需求变动的常态,以说明为什么我们需要学习「依赖注入」(dependency injection:简称 DI)来改善设计 ...

  3. Dependency Injection 筆記 (4)

    续上集未完的相关设计模式... (本文摘自電子書:<.NET 依賴注入> Composite 模式 延续先前的电器比喻.现在,如果希望 UPS 不只接计算机,还要接电风扇.除湿机,可是 U ...

  4. Dependency Injection

    Inversion of Control - Dependency Injection - Dependency Lookup loose coupling/maintainability/ late ...

  5. Ninject学习(一) - Dependency Injection By Hand

    大体上是把官网上的翻译下而已. http://www.ninject.90iogjkdcrorg/wiki.html Dependency Injection By Hand So what's Ni ...

  6. MVC Controller Dependency Injection for Beginners【翻译】

    在codeproject看到一篇文章,群里的一个朋友要帮忙我翻译一下顺便贴出来,这篇文章适合新手,也算是对MEF的一个简单用法的介绍. Introduction In a simple stateme ...

  7. 控制反转Inversion of Control (IoC) 与 依赖注入Dependency Injection (DI)

    控制反转和依赖注入 控制反转和依赖注入是两个密不可分的方法用来分离你应用程序中的依赖性.控制反转Inversion of Control (IoC) 意味着一个对象不会新创建一个对象并依赖着它来完成工 ...

  8. [转载][翻译] IoC 容器和 Dependency Injection 模式

    原文地址:Inversion of Control Containers and the Dependency Injection pattern 中文翻译版本是网上的PDF文档,发布在这里仅为方便查 ...

  9. 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 ...

随机推荐

  1. 【14.67%】【codeforces 615D】Multipliers

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  2. Risk Adaptive Information Flow Based Access Control

    Systems and methods are provided to manage risk associated with access to information within a given ...

  3. 【33.00%】【vijos P1002】过河

    描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上 ...

  4. jQuery插件开发小总结

    另一篇 jQuery插件开发通常有3种方式 通过$.extend()来扩展jQuery 通过$.fn 向jQuery添加新的方法 通过$.widget()应用jQuery UI的部件工厂方式创建 通常 ...

  5. Information centric network (icn) node based on switch and network process using the node

    The present invention relates to an apparatus for supporting information centric networking. An info ...

  6. NYOJ 24 素数的距离问题

    素数的距离问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:2 描写叙述 如今给出你一些数.要求你写出一个程序,输出这些整数相邻近期的素数,并输出其相距长度.假设左右有等距离长 ...

  7. 记一次虚拟化环境下Windows IO性能的解析

    前言随着云计算技术与服务的发展和进步,越来越多的客户选择将业务部署到云端.但由于引入了虚拟化层,在业务部署过程中经常会遇到IO问题,通常也不易调试.本文主要介绍利用perf.systemtap等工具, ...

  8. WPF自定义控件 使用阿里巴巴图标

    原文:WPF自定义控件 使用阿里巴巴图标 上一篇介绍了 WPF自定义控件 按钮 的初步使用,在进一步介绍WPF自定义控件 按钮之前,先介绍一下如何在WPF项目中使用阿里巴巴图标,方便以后做示例. 1. ...

  9. TestNg依靠先进的采用强制的依赖,并依赖序列的------TestNg依赖于特定的解释(两)

    原创文章,版权所有所有,转载,归因:http://blog.csdn.net/wanghantong TestNg使用dependsOnGroups属性来进行依赖測试, 測试方法依赖于某个或某些方法, ...

  10. Emgu-WPF 激光雷达研究-移动物体跟踪2

    原文:Emgu-WPF 激光雷达研究-移动物体跟踪2 初步实现了去燥跟踪,并用圆点标注障碍物 https://blog.csdn.net/u013224722/article/details/8078 ...