Dependency Injection 筆記 (4)
续上集未完的相关设计模式...
(本文摘自電子書:《.NET 依賴注入》
Composite 模式
延续先前的电器比喻。现在,如果希望 UPS 不只接计算机,还要接电风扇、除湿机,可是 UPS 却只有两个电源输出孔,怎么办?
我们可以买一条电源延长线,接在 UPS 上面。如此一来,电风扇、除湿机、和计算机便都可以同时插上延长线的插座了。这里的电源延长线,即类似Composite Pattern(组合模式),因为电源延长线本身又可以再连接其他不同厂牌的延长线(这又是因为插座皆采用相同接口),如此不断连接下去。
呃….延长线的比喻有个小问题:它在外观上看起来也像是层层串接,容易和 Decorator 模式混淆。事实上,这两种设计模式在结构上的确有相似之处。下图所示为 Composite 模式的结构图。

由此结构图可以看得出来,Composite 模式 其实是个树状结构,呈现的是「整体-包含」(whole-part)的关系。树上的每个节点(Leaf)都实现了相同的接口,而每个节点又可以包含多个子节点;就像档案目录结构那样,每个文件夹底下都可以有零至多个文件夹。相较之下,Decorator 模式则是让装饰者看起来长得和被装饰者一样,但其实加上了额外的修饰。
Adapter 模式
当你的手机没电,需要充电时,就算有电源延长线也没用,因为手机充电时所需的电压并不是一般家庭用电的 110 伏特交流电压。此时我们通常会使用手机随附的变压器(adapter),将变压器的电源插头插在墙壁的电源插座,然后将变压器的另一端连接至手机。像这样把一种规格(接口)转换成另一种规格的设计,就叫做Adapter Pattern(转换器模式)。下图所示为 Adapter 模式的结构。

仍使用先前 logging 范例来说明。假设我们没有实现自己的 logging API,而是直接使用第三方组件。然而,考虑到将来很可能会改用另一套 logging 组件,于是决定使用 Adapter 模式来保护自己的代码。首先,必须先订出 logging API 的接口,让应用程序只针对此接口来写入 log。此接口只定义了一个写入日志的方法,叫做 Log,参考以下程序片段。
public interface ILogger
{
void Log(string msg);
}
接着设计 Adapter 类型。此类型须实现 ILogger 接口,并且在 Log 方法中转而调用第三方组件的方法。代码如下:
public class CommonLogger : ILogger
{
private ThirdPartyLogger logger = new ThirdPartyLogger(); public void Log(string msg)
{
logger.WriteEntry(msg); // 转调用第三方元件的方法。
}
}
如此一来,以后如果真的需要改用另一套 logging 组件,程序修改的范围就只限定在 CommonLogger 类型而已。
Factory 模式
第一章曾经提过,每当我们在程序中使用 new 运算符来建立类型的实例,我们的代码就在编译时期跟那个类型固定绑(绑定)在一起了。其实用 new 来建立对象还有个缺点:C# 的构造函数名称就是类名,不可任意命名;于是当类型有数个重载的(overloaded)构造函数时,光是阅读传入构造函数的参数列,有时不见得那么容易明白程序的意图。举例来说:
var user1 = new User("Mike", , true);
var user2 = new User("Jane", , flase);
不如下列代码清楚:
var user1 = UserFactory.CreateAdministrator("Mike", );
var user2 = UserFactory.CreateDomainUser("Jane", );
其中的 UserFactory 就是担任对象工厂的角色,它是个 static 类型,且唯一的任务就是生产特定类型的对象。代码如下所示。
public static class UserFactory
{
public User CreateAdministrator(string name, int id)
{
// 略
} public User CreateDomainUser(string name, int id)
{
// 略
}
}
一般而言,Factory 模式泛指各种能够生产对象的工厂,通常有三种模式:Factory Method(工厂方法)、Simple Factory(简单工厂)、和 Abstract Factory(抽象工厂)。刚才的 UserFactory 就是一个 Simple Factory。
Note: 假设我们完全不知道 DI,或者觉得没必要使用 DI,可是又希望代码不要和特定实现类型绑太紧(不想要直接 new 一个对象),此时 Factory 模式通常是个值得考虑的方案。
实现 Factory Method 模式时,通常会在一个基础类型中定义建立对象的抽象方法(难怪叫做「工厂方法」),然后由各个子类型来实现该方法。若将先前的 UserFactory 改成以 Factory Method 来实现,其类型结构如下图所示。

Abstract Factory 比前面两种工厂模式要稍微复杂一些,它是用来建立多族系的相关或相依对象,且无须指名对象的具象类型。实现此模式时,会将一组建立对象的方法定义成一个接口,代表抽象工厂。然后,你可以编写多个类型来实现该接口,而这些类型的角色就像真实世界中的工厂,类型中的每一个工厂方法则有点像是真实工厂里的一条生产线。当客户端需要建立该族系的对象时,就是利用其中一种具象工厂(concrete factory)来生成对象。此外,由于具象工厂都实现了同一组接口,所以客户端甚至可以在执行时期动态切换成不同的工厂,以建立一组相关的对象。

如果你跟我一样,常常搞混这三种 Factory 模式,我发现《Refactoring to Patterns》这本书的 6.2 节里面有一张简略的结构图挺有用。我依样画了一张,如下图所示,其中的粗黑线代表建立对象的函式。下次忘记时,不妨回来瞄一眼底下这张图,也许能帮你回想起来它们之间的差异。

OK! 设计模式的部分就概略介绍到此,后续章节中如碰到其他模式,也会一并介绍(例如 Strategy、Repository、Service Locator 等等)。
「我曾在这样的十字路口:努力学习各种模式,希望成为一个更好的软件设计师;但现在,为了真正成为更优秀的软件设计师,我必须降低对模式的依赖。」
—— Joshua Kerievsky. 《Refactoring to Patterns》 作者
更多內容請參閱電子書:《.NET 依賴注入》
Dependency Injection 筆記 (4)的更多相关文章
- Dependency Injection 筆記 (3)
续上集.接着要来进一步了解的是 DI 的实现技术,也就是注入相依对象的方式.这里介绍的依赖注入方式,又称为「穷人的 DI」(poor man’s DI),因为这些用法都与特定 DI 工具无关,亦即不使 ...
- Dependency Injection 筆記 (2)
续上集,接着要说明如何运用 DI 来让刚才的范例程序具备执行时期切换实现类型的能力. (本文摘自電子書<.NET 依賴注入>) 入门范例—DI 版本 为了让 AuthenticationS ...
- Dependency Injection 筆記 (1)
<.NET 依賴注入>連載 (1) 本文从一个基本的问题开始,点出软件需求变动的常态,以说明为什么我们需要学习「依赖注入」(dependency injection:简称 DI)来改善设计 ...
- 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 ...
随机推荐
- 【9705】&&【a801】细胞
Time Limit: 10 second Memory Limit: 2 MB 问题描述 一矩形阵列由数字1~9代表细胞,细胞的定义是沿细胞数字上下左右如果还是细胞数字则为同一细胞,求给定矩形阵列的 ...
- 关于生命周期里执行setState
React里生命周期的哪些方法里可以执行setState,这对于很多使用React很长时间的开发都是个迷惑的问题. 先看一下完整的生命周期. 再看两篇文章 React componentDidUpda ...
- 左右Cwnd::Create()功能出现afxwin1.inl line:21错误的解决方案
我最近在调试dll时刻,有一种模糊的断言错误,它是由主程序创建MFC 扩张DLL控制出口(从控制继承CWnd分类)时刻,呼叫Create()下列说法错误的功能: watermark/2/text/aH ...
- cordova插件整理
原文:cordova插件整理 1.获取当前应用的版本号 cordova plugin add cordova-plugin-app-version 2.获取网络连接信息 cordova plugin ...
- Yii2.0
Yii2.0基础框架 缘起 因为一个月的短暂停留,我在给朋友搞事情,所以Yii系列的文章耽搁了很长时间,现在又重拾当时的知识,给大伙好好撸下这一系列的博客 提起Yii,虽然是国外的开发者搞的,但是 ...
- Python中 如何将一个字符串分成一个个字符
其实 一个字符串 实质也是 一个列表 就很简单了: a = ' for item in a: print(item) 打印结果: 121512 如果进而要统计字符出现的次数 , 那就很简单了.
- Android中使用ListView实现自适应表格
GridView比ListView更容易实现自适应的表格,但是GridView每个格单元的大小固定,而ListView实现的表格可以自定义每个格单元的大小,但因此实现自适应表格也会复杂些(格单元大小不 ...
- matplotlib tricks(关闭坐标刻度、坐标轴不可见)
plt.gray():只有黑白两色,没有中间的渐进色 1. 关闭坐标刻度(plt 与 AxesSubplot) plt plt.xticks([]) plt.yticks([]) 关闭坐标轴: plt ...
- 得知OpenCV研究报告指出系列(一)VS2010+OpenCV2.4.9环境配置
学习OpenCV,首先,当然,要知道如何配置的环境. 余系统的软件和硬件环境,如以下: 以本人的配置环境为例,配置过程例如以下. 第一步 下载及解压OpenCV源代码 尽管非常多第三方站点及一些学习论 ...
- 简明Python3教程 7.运算符和表达式
简介 你写的大多数逻辑行都包含表达式.表达式的一个简单例子是2 + 3.一个表达式可分为操作符和操作数两部分. 操作符的功能是执行一项任务:操作符可由一个符号或关键字代表,如+ .操作符需要数据以供执 ...