上集未完的相关设计模式...

(本文摘自電子書:《.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)的更多相关文章

  1. Dependency Injection 筆記 (3)

    续上集.接着要来进一步了解的是 DI 的实现技术,也就是注入相依对象的方式.这里介绍的依赖注入方式,又称为「穷人的 DI」(poor man’s DI),因为这些用法都与特定 DI 工具无关,亦即不使 ...

  2. Dependency Injection 筆記 (2)

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

  3. Dependency Injection 筆記 (1)

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

  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. cordova 生成发行版apk,并添加证书 – 畅玩Coding

    原文:cordova 生成发行版apk,并添加证书 – 畅玩Coding 首先jdk生成证书. 1.进入jdk安装目录 D:\Java\jdk1.7.0\bin 2.执行命令 keytool -gen ...

  2. CentOS 配置epel源

    先查询下有没有epel rpm -qa|grep epel 没有的话到官网https://fedoraproject.org/wiki/EPEL下载rpm包 然后 rpm -ivh 安装 安装完毕后到 ...

  3. CUDA中block和thread的合理划分配置

    CUDA并行编程的基本思路是把一个很大的任务划分成N个简单重复的操作,创建N个线程分别执行执行,每个网格(Grid)可以最多创建65535个线程块,每个线程块(Block)一般最多可以创建512个并行 ...

  4. 转:spring MVC HTTP406 Not Acceptable

    springMVC框架下,ajax请求,在用json对象返回的时候,可以用如下的形式: response.setContentType("text/html;charset=UTF-8&qu ...

  5. WPF中,怎样将XAML代码加载为相应的对象?

    原文:WPF中,怎样将XAML代码加载为相应的对象? 在前面"在WPF中,如何得到任何Object对象的XAML代码?"一文中,我介绍了使用System.Windows.Marku ...

  6. c#开发移动APP-Xamarin入门扩展

    原文:c#开发移动APP-Xamarin入门扩展 这节主要演示了如何通过添加第二个屏幕来跟踪应用程序的call历史来扩展Phoneword应用程序.最终如下: 按如下步骤扩展Phoneword 在Ph ...

  7. qt4.8.7 源码在win7+vs2010环境的x64编译(qt 64位)

    由于qt官网上,没有直接提供x64的安装包,但由于项目需要x64的qt,所以,小编不得不下载qt的源码,经历了一次长达约4个小时的编译过程.今年国庆7天,就遭这事上了,哈哈~~~ 几个下载链接: qt ...

  8. Qt5官方demo分析集10——Qt Quick Particles Examples - Emitters

    此系列的所有文章都可以在这里查看http://blog.csdn.net/cloud_castle/article/category/2123873 前段时间去听了Qt在北京的开发人员大会,感觉QML ...

  9. Android 混淆代码汇总

    为了防止别人对自己被盗的劳动,混淆代码可以被反编译可以有效地防止,以下在下面的代码混乱总结的步骤: 1. 大家可能已经注意到一个新的项目将在下面看到的物品都有这个proguard-project.tx ...

  10. httpclient POST请求(urlencoded)

    搬砖搬砖~ Content-Type:application/x-www-form-urlencoded的请求如下 var nvc = new List<KeyValuePair<stri ...