上集。接着要来进一步了解的是 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. hbase 从hbase上读取数据写入到hdfs

    Mapper package cn.hbase.mapreduce.hb2hdfs; import java.io.IOException; import org.apache.hadoop.hbas ...

  2. rac下一个/tmp/bootstrap权限问题

    原创作品,离 "深蓝blog" 博客,欢迎转载,请务必注明转载如下源,否则追究其版权责任. 深蓝的blog:http://blog.csdn.net/huangyanlong/ar ...

  3. Android平台第三方应用分享到微信开发

    一.申请APPID 微信公共平台和微博分享一样,也需要申请一个ID,来作为调起微信.分享到微信的唯一标识. 申请微信APPID可以到微信平台http://open.weixin.qq.com/app/ ...

  4. WPF 等距布局

    原文:WPF 等距布局 本文告诉大家如何使用 WPF 的自定义布局做等距布局. 实际做的效果很简单,因为在开发我容易就用到了等距的控件.等距控件就是在指定的宽度下,平均把控件放在水平的地方,这样相等于 ...

  5. ASP.NET Core & Docker & Jenkins 零基础持续集成实战

    原文:ASP.NET Core & Docker & Jenkins 零基础持续集成实战 一.本系列教程说明 源代码管理工具:Gogs 持续集成工具:Jenkins 容器:Docker ...

  6. linux下安装sqlite3

    1.介绍:sqlite3是linux上的小巧的数据库,一个文件就是一个数据库.2.安装:  要安装sqlite3,可以在终端提示符后运行下列命令:  sudo apt-get install sqli ...

  7. phpcms视图查询数据

    {pc:get sql="SELECT * FROM phpcms WHERE id in ($id) ORDER BY listorder ASC LIMIT 0, 1--"re ...

  8. [演示示例程序]Objective-C受委托的设计模式(牛仔女孩)

    今天整理电脑打开一次自我Objective-C当编写一个实践设计模式委托一个小程序,在po快来分享.也复习一下OC中的托付. Objective-C中的托付设计模式是和协议分不开的. 协议呢.就是使用 ...

  9. Numpy Usage Introduction

    Reference : http://my.oschina.net/u/175377/blog/74406 试验性的Numpy教程 原文来自Tentative NumPy Tutorial 目录 先决 ...

  10. Hopfield 神经网络及稳态性的证明

    根据其提出者,John Joseph Hopfield 命名.Hopfield 在 1982 年提出的划时代的:Neural networks and physical systems with em ...