上集。接着要来进一步了解的是 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. CentOS下Apache的停止和卸载

    昨晚搞到一台全球性价比最高的服务器,折腾一晚上,好不容易把node服务开启了,结果访问不了我的网站!!! 访问我的网站,显示的是一个Apache欢迎页面.我想,是不是像之前那样,80端口没有开放,然后 ...

  2. CentOS 7 部署 ASP.NET Core 应用程序

    原文:CentOS 7 部署 ASP.NET Core 应用程序 看了几篇大牛写的关于 Linux 部署 ASP.NET Core 程序的文章,今天来实战演练一下.2017年最后一个工作日,提前预祝大 ...

  3. CSS动作

    5.过渡 tansition  (由一个状态过渡到另外一个状态的过程)             transition            过渡                 参数1:        ...

  4. 矿Spring入门Demo

    第一步:输入Spring jar 包裹 Spring核心包(4个)  日志包(2个)  jdbc模板支持(1个)  spring-jdbc-3.2.0.RELEASE.jar  模板相关事务处理包(1 ...

  5. VSCode 小鸡汤 第00期 —— 安装和入门

    简介 这将是一个新的系列,将会以 Visual Studio Code(后文都简称为 VSCode 啦)的操作,环境配置,插件介绍为主,为大家不定期的介绍 VSCode 的一些操作技巧,所以取名 VS ...

  6. PYC文件简介

    PYC文件简介¶ 不说废话,这里说的pyc文件就是 Python 程序编译后得到的字节码文件 (py->pyc). 基本格式¶ pyc文件一般由3个部分组成: 最开始4个字节是一个Maigc i ...

  7. RTB业务知识2-Open-RTB全景

    一个.前言 openrtb这是一套开源的竞价广告系统,来自IAB贡献,井. 有许多值太借鉴,提供sdk api接口文档介绍,整理了相关的资料.主要包含其生态图体系.业务流程和基本的对象模型和数据模型. ...

  8. crossplatform----文本编辑器工具Atom安装

    1.简介 Atom 是 Github 专门为程序员推出的一个跨平台文本编辑器.具有简洁和直观的图形用户界面,并有很多有趣的特点:支持CSS,HTML,JavaScript等网页编程语言.它支持宏,自动 ...

  9. hdu4614 二分法+段树

    意甲冠军:给你1-n花瓶   .起初,所有的空,今天,有两种操作模式, 1:从花瓶a開始插入b朵花          假设不能插进去  输出字符串  否则输出最多插入的起点和终点: 2:把a-b的花瓶 ...

  10. Matlab随笔之线性规划

    原文:Matlab随笔之线性规划   LP(Linear programming,线性规划)是一种优化方法,在优化问题中目标函数和约束函数均为向量变量的线性函数,LP问题可描述为:min xs.t. ...