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", 101, true); var user2 = new User("Jane", 102, flase);
不如下列代码清楚:
var user1 = UserFactory.CreateAdministrator("Mike", 101); var user2 = UserFactory.CreateDomainUser("Jane", 102);
其中的 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 依賴注入》
posted on 2014-08-13 21:59 MichaelTsai 阅读(827) 评论(2) 编辑 收藏 举报