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

(本文摘自電子書:《.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. instsrv.exe用法

    这个小工具是用以安装和卸载可执行的服务和指派服务名给这些可执行的服务的.     一:绑定程序和服务 这里我们设定要将F:\cpu.exe 以 abc 的名称显示作为服务的话,我们应当这样子做: 在开 ...

  2. mac在终端打开应用程序

    今天研究了下mac终端的启动流程.以下以sublime为例,介绍怎么在mac的终端中加入app启动方法. 方法1 :使用"open -a /Applications/Sublime\ Tex ...

  3. Maven打包不打test,Maven中-DskipTests和-Dmaven.test.skip=true的区别

    在使用mvn package进行编译.打包时,Maven会执行src/test/java中的JUnit测试用例, 有时为了跳过测试,会使用参数-DskipTests和-Dmaven.test.skip ...

  4. 一句话的设计模式(JAVA版)

    ·结构型模式: o适配器:用来把一个接口转化成另一个接口,如 java.util.Arrays#asList(). o桥接模式:这个模式将抽象和抽象操作的实现进行了解耦,这样使得抽象和实现可以独立地变 ...

  5. 【struts2+hibernate4】小型电子商务站点

    这里使用的是struts2和hibernate4两个框架开发的一个小型电子商务站点,数据库方面我也会给出对应的代码. 总之使用的是:struts2+hibernate4+jsp+MySQL+tomca ...

  6. springCloud跨域访问

    转自:http://blog.csdn.net/wangkang80/article/details/72829390 什么是跨域? 假设你在http://xxx.com/test/下有一个js文件, ...

  7. C++于public、protected和private说明(From MSDN)

    public(C# 參考): https://msdn.microsoft.com/zh-cn/library/yzh058ae.aspx protected(C# 參考):https://msdn. ...

  8. 像职业选手样编码:地道Python

    Code Like a Pythonista: Idiomatic Python David Goodger goodger@python.org http://python.net/~goodger ...

  9. Bootstrap Edit 使用方法

    Getting Started <!-- rounded edit text --> <com.beardedhen.androidbootstrap.BootstrapEditTe ...

  10. 在实现视频播放器的步骤client(三)风行网络电影列表

    (三) 今日热门电影实现这个功能.主要从server获取数据.然后显示在屏幕上.虽然说是从这个server获取电影信息数据,但,不实际的http相关知识,我们直接sdk包(56网络提供api),你将能 ...