意图 :将复杂对象的构建与表示分离,是同样的构建过程可以创建过程可以创建不同的表示。需要注意以下几点:
(1 )。构建与表示分离:表明生成器模式的结构,构建过程被封装在导航器中,生成器则实现具体的表示。
(2 )。同样的构建过程:生成器模式关注的是构建过程,即构建过程是相同的。
(3 )。不同的表示:生成器模式并不在意产生对象的结果,其构造的产品不一定有相同的类型。
使用场合 :创建复杂对象的算法应该独立于该对象的组成部分及其装配方式,以及构造过程必须允许被构造的对象有不同表示情况下使用生成器模式。 主要适用于创建一些复杂的对象,这些对象的内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
结构 :
Bulider : 生成器, 为创建Product 对象的各个部件指定抽象接口。
ConcreteBuilder : 具体实现者, 实现Builder 的接口以构造和装配该产品的各个部件,定义并明确他所创建的表示,并提供了一个检索产品的接口。
Director :导航器,构造了一个使用Builder 接口的对象。
Product :表示并构建的复杂对象。ConcreteBuilder 创建该产品的内部表示并定义他的装配过程,包含定义组成部件的类,以及将这些部件装配成最终产品的接口。
下面是建造这模式的基本代码,Product产品类,由多个部件组成:
public class Product
{
IList<string> parts = new List<string>();
public void Add(string part)
{
parts.Add(part);
}
public void Show()
{
Console.WriteLine("\n产品 创建 ----");
foreach (string part in parts)
{
Console.WriteLine(part);
}
}
}
public abstract class Builder
{
public abstract void BuildPartA();
public abstract void BuildPartB();
public abstract Product GetResult();
}
public class ConcreteBuilder1:Builder
{
private Product product = new Product();
public override void BuildPartA()
{
product.Add("部件A");
}
public override void BuildPartB()
{
product.Add("部件B");
}
public override Product GetResult()
{
return product;
}
}
public class ConcreteBuilder2:Builder
{
private Product product = new Product();
public override void BuildPartA()
{
product.Add("部件X");
}
public override void BuildPartB()
{
product.Add("部件Y");
}
public override Product GetResult()
{
return product;
}
}
public class Director
{
public void Construct(Builder builder)
{
builder.BuildPartA();
builder.BuildPartB();
}
}
static void Main(string[] args)
{
Director director = new Director();
Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2();
director.Construct(b1);
Product p1 = b1.GetResult();
p1.Show();
director.Construct(b2);
Product p2 = b2.GetResult();
p2.Show();
Console.Read();
}
简单的脸谱生成,将脸谱生成的过程分解为画脸、画眼睛、画鼻子、画耳朵和画嘴巴。其结构如下:
具体代码:
首先创建一个抽象的生成器,命名为FaceBuilder:
public abstract class FaceBuilder
{
public abstract void BuildFace();
public abstract void BuildEye();
public abstract void BuildNose();
public abstract void BuildEars();
public abstract void BuildMouth();
}
然后创建一个具体的生成器,使用GUI+绘制脸谱:
public class GUIFaceBuilder:FaceBuilder
{
private Graphics grap;
private Pen p;
public GUIFaceBuilder(Graphics g)
{
grap = g;
p = new Pen(Color.Blue);
}
public override void BuildFace()
{
grap.DrawEllipse(p, 100, 100, 200, 300);
}
public override void BuildEye()
{
grap.DrawEllipse(p, 125, 220, 50, 30);
grap.DrawEllipse(p, 225, 220, 50, 30);
}
public override void BuildNose()
{
grap.DrawEllipse(p, 190, 250, 20, 50);
}
public override void BuildEars()
{
grap.DrawEllipse(p, 80, 250, 20, 40);
grap.DrawEllipse(p, 300, 250, 20, 40);
}
public override void BuildMouth()
{
grap.DrawEllipse(p, 190, 350, 20, 1);
}
}
接着创建绘制导航:
public class FaceDirector
{
private FaceBuilder fbuild;
public FaceDirector(FaceBuilder b)
{
this.fbuild = b;
}
public void CreateFace()
{
fbuild.BuildFace();
fbuild.BuildEars();
fbuild.BuildEye();
fbuild.BuildMouth();
fbuild.BuildNose();
}
}
最后可以在一个窗体中调用:
FaceDirector c = new FaceDirector(b);
c.CreateFace();
Builder模式的意图:
将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。——《设计模式》
1:对象构建过程被封装在Director中,具体的表示由生成器(ConcreteBuilder)完成。从而构建与表示彻底分离。
2:相同的Director(构建过程)。
3:不同的表示(ConcreteBuilder),上文的ConcreteBuilder对象与ConcreteBuilderB对象具有不同的类型,也就是说,Builder模式不关心产生的结果。
和抽象工厂的区别:抽象工厂生成一个产品系列,接口相同;而Builder模式产生的成品可以有不同接口,只要创建过程是同样的方法就可以。还有就是,如果是抽象工厂,Client端不需要知道返回对象的具体类型,因为抽象工厂构造的是具有相同接口的产品系列;如果是Builder模式,用户必须知道返回对象的具体类型,因为不同的生成器返回的类型是不同的。
注意,Builder模式=Builder+Director,二者缺一不可;.NET平台有一个StringBuilder,这只是Builder,与模式没关系。
另外,.NET平台中,Page类中的OnInit()等方法的实现。我们在写一个Web页面的时候。他的codebehind代码都是继承System.Web.UI.Page基类的。OnInit()函数是可以重写的,这也是Builder模式的应用之一:导航器和Builder接口封装在Framework中。生成器也在封装在Framework中,只是对外暴露一个虚方法实现OnInit(),由你来实现。Client端调用机制也在Page生成周期中自动实现。——.NET充分利用了模式来方便开发者,但是不说清楚,一般开发人是不会留意的。
补注:
1. GetResult()方法的位置。
文中讲的是放在具体生成器中。可不可以放在Builder接口中呢,或者省略这个方法呢?答案是可以的。
1)当不需要返回一个产品时,就不需要GetResult这个方法。比如说文中讲的画脸谱,在BuildPart()方法中就已经实现了g.Draw这样的操作,这时候是否生成产品就不重要了。
2)当需要返回一个产品时,分两种情况:如果这些产品具有相同的接口,则可以将GetResult()方法抽象到Builder接口,因为返回相同的产品类型;但是如果这些产品的接口不同,则必须各自实现在具体的生成器中。
2. Builder的缺点:不方便添加一个新BuilderPart()方法。这是因为,如果需要增加新的BuilderPart(),要同时改动接口,原有的生成器,还有导航器,也就是说,所有的都要改变,这是不可以的。
3. AbstractFactory认为构造过程和生成都是很简单的,这时候,如果这些产品具有相同的接口,使用抽象工厂可好,而不使用Builder。