描述
创建者模式的意图是:
将复杂对象的创建与其表现分离,以使相同的创建过程能够创建不同的表现。
GOF的例子是一个RTF文档交换格式的阅读器,这种格式支持多种不同的表现类型,例如ASCII文本以及GUI图形挂件,可以让用户根据需要编辑文本。
下图是该模式的UML。在GOF的例子中,阅读器扮演了下图中Director的角色。每个产品对应了不同的创建者,图中的产品(Product)就是例子中的不同的表现类型。Director使用具体的创建者(图中Concrete Builder)来创建产品的每个部分并通过GetResult汇集最终的结果。在创建过程中,Director提供向创建者提供创建产品所需的输入。在上面的例子中,阅读器从输入流中读取字符,识别应当表现的类型(文本或者图形),并且据此在Builder中调用相应的BuildPart。
如模式意图所述,创建者封装了创建过程,隐藏了不同表现类型的内部数据结构,同时Director能够使用相同的创建过程来创建不同的表现类型。此处通常不存在可以抽象的产品,因为表现类型往往区别很大,无法继承自一个抽象类。
分析
创建者意图是封装其自身的创建过程,通过类方法是实现这一目的的合适的抽象机制。创建过程的成果在细节上 有很大差别,只有输入在不同的具体创建者(Concrete Builder)中保持一致。Scala在实现这些概念的时候并没有什么不同,同样是使用方法。我们可以尝试使用抽象类型来表示构建过程的互相依赖,但是因为输出差别太大,这样做意义不大。
GOF例子中初始的代码使用了双重分派。Director类包含了一个switch声明,对不同的输入种类提供不同的分支处理,调用创建者不同的构造方法。下面的C++模式的例子中t.Type作为switch声明的控制表达式,它必须是整数类型,所以不能使用真实的类型,只能是t对象的一个属性。
switch t.Type {
CHAR: builder->Convertcharacter(t.Char);
FONT: builder->ConvertFontChange(t.Font);
}
//多重分派
builder->Convert(t)
当然这需要两个都为Convert名称的方法,都通过一个参数提供不同的类型。注意后一个t与前一个个t的类型是不同的。
Scala的模式匹配功能可以轻松实现多重分派
t match {
case c@ Character()=>convert(c)
case f@ Font()=>convert(f)
}
def convert(t:Font)=println("Convert font")
def convert(t:Character)=println(Converting character")
使用Scala的模式匹配技术,匹配运行时类型,而不是类中用作描述类型的成员。因此也无须再使用多重分配builder->Convert(t),这比本来的代码更好。
模块化
实例化之间的不同之处是在不同的创建者中被封装的创建方法,以及创建的产品和每个创建方法的输入。对类的一些列方法进行抽象是不可能的,这表明了无法对创建者进行模块化。因为不能为依赖于创建者细节的其他的角色提供任何模块。
小结
GOF提出的创建者的解决方案得益于多重分派(double dispatch),但Scala并不能直接提供这一特性。分析表明多重分派可以被Scala的模式匹配代替。
与主流的面向对象语言相比,Scala没有为实现此模式提供新的方式。