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 ...
随机推荐
- 空间同构(isomorphic)
1. introduction 对于一个 M22 矩阵空间,其 dimM22=4,基的构成如下: {(1000)(0010)(0100)(0001)} 则:M22≅R4 对于 P3 多项式空间,也即: ...
- 使用Fiddler对android应用抓包 专题
工作原理 先上个图 此图一目了然,可以看出fiddler在请求中所处的位置,我们就可以确定它能干些什么. WinInet(“Windows Internet”)API帮助程序员使用三个常见的Inter ...
- HDU 1143 Tri Tiling (递推)
Tri Tiling Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total ...
- Java--ConcurrentHashMap原理分析
一.背景: 线程不安全的HashMap 因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap. 效率低下的H ...
- jxl导出Excel
首先先在自己工程中导入jxl的jar包: 疯狂google后找到一段别人的导出excel方法,先备份于下面: import java.io.File; import java.io.FileOutpu ...
- Method of offloading iSCSI TCP/IP processing from a host processing unit, and related iSCSI TCP/IP offload engine
A method of offloading, from a host data processing unit (205), iSCSI TCP/IP processing of data stre ...
- Emoji:搜索将与您找到表情符号背后的故事
眼下.秉已经开始支持emoji搜索,这意味着,你可以插入或粘贴系列emoji表情,让我们的爱.微笑.食品等..些表情随意组合,必应总会带给你非常多有趣的但却没有不论什么实际用途的搜索结果. 这是一项非 ...
- Leetcode 144 Binary Tree Preorder Traversal 二叉树
二叉树的基础操作:二叉树的先序遍历(详细请看数据结构和算法,任意本书都有介绍),即根,左子树,右子树,实现方法中还有用栈实现的,这里不介绍了 /** * Definition for binary t ...
- 简明Python3教程 9.函数
简介 函数是程序的可复用片段,允许你为语句块赋予名字之后在程序的任何地方运行它们任意次,这称做函数调用. 我们已经使用过一些内建函数,例如len和range等. 函数也许是任何有意义的软件中最重要的构 ...
- WPF 3D动态加载模型文件
原文:WPF 3D动态加载模型文件 这篇文章需要读者对WPF 3D有一个基本了解,至少看过官方的MSDN例子. 一般来说关于WPF使用3D的例子,都是下面的流程: 1.美工用3DMAX做好模型,生成一 ...