FrankFei,2007年3月12日
一、概述
在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法确相对稳定。如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?这就是要说的建造者模式。
建造者模式可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。
对象性质的建造
有些情况下,一个对象会有一些重要的性质,在它们没有恰当的值之前,对象不能作为一个完整的产品使用。比如,一个电子邮件有发件人地址、收件人地址、主题、内容、附录等部分,而在最起码的收件人地址未被赋值之前,这个电子邮件不能发出。
有些情况下,一个对象的一些性质必须按照某个顺序赋值才有意义。在某个性质没有赋值之前,另一个性质则无法赋值。这些情况使得性质本身的建造涉及到复杂的商业逻辑。
这时候,此对象相当于一个有待建造的产品,而对象的这些性质相当于产品的零件,建造产品的过程就是组合零件的过程。由于组合零件的过程很复杂,因此,这些"零件"的组合过程往往被"外部化"到一个称作建造者的对象里,建造者返还给客户端的是一个全部零件都建造完毕的产品对象。
之所以使用"建造者"而没有用"生成器"就是因为用零件生产产品,"建造"更为合适,"创建"或"生成"不太恰当。
二、意图
将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
三、Builder模式的结构
Builder:为创建Product对象的各个部件指定抽象接口。
ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。
Director:构造一个使用Builer接口的对象。
Product:表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,以及将这些部件装配成最终产品的接口。
四、举例
// Builder pattern -- Structural example
using System;
using System.Collections;
// "Director"
class Director
{
// Methods
public void Construct( Builder builder )
{
builder.BuildPartA();
builder.BuildPartB();
}
}
// "Builder"
abstract class Builder
{
// Methods
abstract public void BuildPartA();
abstract public void BuildPartB();
abstract public Product GetResult();
}
// "ConcreteBuilder1"
class ConcreteBuilder1 : Builder
{
// Fields
private Product product;
// Methods
override public void BuildPartA()
{
product = new Product();
product.Add( "PartA" );
}
override public void BuildPartB()
{
product.Add( "PartB" );
}
override public Product GetResult()
{
return product;
}
}
// "ConcreteBuilder2"
class ConcreteBuilder2 : Builder
{
// Fields
private Product product;
// Methods
override public void BuildPartA()
{
product = new Product();
product.Add( "PartX" );
}
override public void BuildPartB()
{
product.Add( "PartY" );
}
override public Product GetResult()
{
return product;
}
}
// "Product"
class Product
{
// Fields
ArrayList parts = new ArrayList();
// Methods
public void Add( string part )
{
parts.Add( part );
}
public void Show()
{
Console.WriteLine( " Product Parts -------" );
foreach( string part in parts )
Console.WriteLine( part );
}
}
/// <summary>
/// Client test
/// </summary>
public class Client
{
public static void Main( string[] args )
{
// Create director and builders
Director director = new Director( );
Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2();
// Construct two products
director.Construct( b1 );
Product p1 = b1.GetResult();
p1.Show();
director.Construct( b2 );
Product p2 = b2.GetResult();
p2.Show();
}
}
using System;
using System.Collections;
// "Director"
class Director
{
// Methods
public void Construct( Builder builder )
{
builder.BuildPartA();
builder.BuildPartB();
}
}
// "Builder"
abstract class Builder
{
// Methods
abstract public void BuildPartA();
abstract public void BuildPartB();
abstract public Product GetResult();
}
// "ConcreteBuilder1"
class ConcreteBuilder1 : Builder
{
// Fields
private Product product;
// Methods
override public void BuildPartA()
{
product = new Product();
product.Add( "PartA" );
}
override public void BuildPartB()
{
product.Add( "PartB" );
}
override public Product GetResult()
{
return product;
}
}
// "ConcreteBuilder2"
class ConcreteBuilder2 : Builder
{
// Fields
private Product product;
// Methods
override public void BuildPartA()
{
product = new Product();
product.Add( "PartX" );
}
override public void BuildPartB()
{
product.Add( "PartY" );
}
override public Product GetResult()
{
return product;
}
}
// "Product"
class Product
{
// Fields
ArrayList parts = new ArrayList();
// Methods
public void Add( string part )
{
parts.Add( part );
}
public void Show()
{
Console.WriteLine( " Product Parts -------" );
foreach( string part in parts )
Console.WriteLine( part );
}
}
/// <summary>
/// Client test
/// </summary>
public class Client
{
public static void Main( string[] args )
{
// Create director and builders
Director director = new Director( );
Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2();
// Construct two products
director.Construct( b1 );
Product p1 = b1.GetResult();
p1.Show();
director.Construct( b2 );
Product p2 = b2.GetResult();
p2.Show();
}
}
现在我们来分析一下上面的例子:
假设要组装一辆自行车,并且自行车就是车轮和车架组成,自行车有永久牌和凤凰牌
Builder对应于组装自行车所使用的车轮和车架
ConcreteBuiler1对应于永久牌车轮和车架,同时可以返回一辆永久牌自行车,ConcreteBuiler2对应于凤凰牌车轮和车架,同时可以返回一辆凤凰牌自行车
Product对应于自行车
Director表示组装过程
此时我们再来理解下面这句话:“在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法确相对稳定。”
自行车就是“一个复杂对象”,它有车轮和车架组成,但不管车轮和车架这两个部件怎么变化,生产一辆自行车的过程是不会变的,即组装过程是不会变的。
五、优点
1、建造者模式的“加工工艺”是暴露的,这样使得建造者模式更加灵活。
2、解耦了组装过程和创建具体部件,使得我们不用去关心每个部件是如何组装的。--上面对自行车的分析可以很清楚的看出这点。
六、缺点
1、上面的第1点优点也可以说是建造者模式的缺点,因为“加工工艺”的暴露,使此模式不太“优雅”。
七、适用性
1、需要生成的产品对象有复杂的内部结构。
2、需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。
3、在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。
2、需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。
3、在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。
八、实现要点
1、建造者模式主要用于“分步骤构建一个复杂的对象”,在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。
2、产品不需要抽象类,特别是由于创建对象的算法复杂而导致使用此模式的情况下或者此模式应用于产品的生成过程,其最终结果可能差异很大,不大可能提炼出一个抽象产品类。
3、创建者中的创建子部件的接口方法不是抽象方法而是空方法,不进行任何操作,具体的创建者只需要覆盖需要的方法就可以,但是这也不是绝对的,特别是类似文本转换这种情况下,缺省的方法将输入原封不动的输出是合理的缺省操作。
3、创建者中的创建子部件的接口方法不是抽象方法而是空方法,不进行任何操作,具体的创建者只需要覆盖需要的方法就可以,但是这也不是绝对的,特别是类似文本转换这种情况下,缺省的方法将输入原封不动的输出是合理的缺省操作。
4、前面我们说过的抽象工厂模式(Abtract Factory)解决“系列对象”的需求变化,Builder模式解决“对象部分”的需求变化,建造者模式常和组合模式(Composite Pattern)结合使用。
九、.Net Framework中的应用
C#中有一个类叫StringBuilder,输入必要的信息后,就可以取出对应的String。(我们可以将Builder理解成电饭锅,给这个Builder放进去米和水,经过Builder的BuildPart后,我们就可以取出香喷喷的米饭了。)
using System;
using System.Text;
class Exam
{
public static void Main()
{
StringBuilder sb = new StringBuilder();
sb.Append('a',2);
sb.Append('b',3);
sb.Append('c',4);
Console.WriteLine(sb.ToString()); //打印出 aabbbcccc
sb.Remove(0, sb.Length); //清除sb中的所有信息
}
}
using System.Text;
class Exam
{
public static void Main()
{
StringBuilder sb = new StringBuilder();
sb.Append('a',2);
sb.Append('b',3);
sb.Append('c',4);
Console.WriteLine(sb.ToString()); //打印出 aabbbcccc
sb.Remove(0, sb.Length); //清除sb中的所有信息
}
}
程序执行结果为: aabbbcccc
十、总结
建造者模式的实质是解耦组装过程和创建具体部件,使得我们不用去关心每个部件是如何组装的。
参考文献
《敏捷软件开发-原则、模式与实践》 Robert C. Martin
《.Net与设计模式》 甄镭
《Java与模式》 阎宏
“网络资源”
参考文献
《敏捷软件开发-原则、模式与实践》 Robert C. Martin
《.Net与设计模式》 甄镭
《Java与模式》 阎宏
“网络资源”