一. 生成器模式简介
生成器模式也有称为建造者模式。生成器模式的意图在于
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示(GoF)。在软件设计中,有时候面临着一个非常复杂的对象的创建工作。这个复杂的对象通常可以分成几个较小的部分,由各个子对象组合出这个复杂对象的过程相对来说比较稳定,但是子对象的创建过程各不相同并且可能面临变化。根据OOD中的OCP原则,我们自然应该对这些子对象的创建过程进行变化封装。
这就是生成器模式的思路。定义一个抽象的建造者的角色(Builder),规定所有具体的建造者都应该具有的功能——这些功能就是如何创建复杂对象的某个特定部分(子对象),而具体如何创建子对象有具体的创建者实现。再定义一个指导者的角色,它把创建者作为工具,知道如何使用这个工具来创建复杂对象。这样,客户在需要创建这个复杂对象的时候,只需要给指导者一个具体的创建者就可以了。至于具体创建者如何创建子对象的细节以及这些子对象之间的差异,不是指导者,也不是客户关心的。
二. 生成器模式实例
还以汽车制造作为例子。假定汽车由三个部件组装而成:车身、引擎和轮胎,每个部件的制造以及最后的组装过程都很复杂。一个品牌汽车销售商一般不会自己来完成这些繁琐的过程,而是把它交给汽车零部件制造商。实际上有许多汽车零部件制造商,可以完成不间部件的生产和组装过程。
那么,客户是怎么获得一辆汽车的呢?首先,客户需要选定一个汽车制造商(比如某品牌),然后在选择一个汽车销售商(经纪人)为我们服务。然后,我们只需要告诉销售商我们需要哪个制造商的汽车,接下来,由销售商来代替我们监督汽车制造商一步一步为我们生产所需要的汽车(可以考虑订单式生产方式或对某部分特殊定制,如加长车身)。汽车生产完成后,由汽车制造厂提车就可以了。
用面向对象的方法对这个问题建模,得到的类图如下:
程序代码如下:
namespace
Builder
...
{ //车身 public class Body ...{ private string name; public string Name ...{ get ...{ return name; } set ...{ name = value; } } public Body(string name) ...{ this.name = name; } } //引擎 public class Engine ...{ private string name; public string Name ...{ get ...{ return name; } set ...{ name = value; } } public Engine(string name) ...{ this.name = name; } } //车轮 public class Wheel ...{ private string name; public string Name ...{ get ...{ return name; } set ...{ name = value; } } public Wheel(string name) ...{ this.name = name; } } //Benz汽车 public class Benz ...{ private Body body; private Engine engine; private IList<Wheel> wheels; public void AddBody(Body body) ...{ this.body = body; } public void AddEngine(Engine engine) ...{ this.engine = engine; } public void AddWheel(Wheel wheel) ...{ if (wheels == null) ...{ wheels = new List<Wheel>(); } wheels.Add(wheel); } public void ShowMe() ...{ if ((this.body == null) || (this.engine == null) || (wheels == null)) ...{ Console.WriteLine("This car has NOT been completed yet!"); } else ...{ Console.WriteLine("This is a car with a " + body.Name + " and a " + engine.Name + "."); Console.WriteLine("This car contains " + wheels.Count + " wheels:"); for (int i = 0; i < wheels.Count; i++) ...{ Wheel wheel = wheels[i]; Console.WriteLine(wheel.Name); } } } } //汽车制造厂抽象定义 public abstract class CarBuilder ...{ public virtual void BuildBody() ...{ ;} //生产车身 public virtual void BuildEngine() ...{ ;} //生产引擎 public virtual void BuildWheel(string name) ...{ ;} //生产车轮 } //Benz汽车制造厂 public class BenzBuilder : CarBuilder ...{ private Benz car; public override void BuildBody() ...{ car = new Car(); car.AddBody(new Body("business car body")); } public override void BuildEngine() ...{ car.AddEngine(new Engine("benz engine")); } public override void BuildWheel(string name) ...{ car.AddWheel(new Wheel(name)); } //生产汽车,Benz汽车装配过程 public Benz GetCar() ...{ //do something like to assemble body and engine together. return car; } } //汽车经销商 public class Producer ...{ private CarBuilder builder; public Producer(CarBuilder builder) ...{ this.builder = builder; } public Car Construct() ...{ builder.BuildBody(); builder.BuildEngine(); builder.BuildWheel("front left"); builder.BuildWheel("front right"); builder.BuildWheel("back left"); builder.BuildWheel("back right"); } } //客户端程序 public class Client ...{ static void Main(string[] args) ...{ BenzBuilder builder = new BenzCarBuilder(); Producer director = new Producer(builder); director.Construct(); Benz car=builder.GetCar(); car.ShowMe(); } }}
这种类结构的设计,有什么好处呢?假设我们现在不想要刚才订的奔驰汽车了,而是想换成一辆路虎越野车怎么办?我们只需要告诉经销商,我们想改变汽车制造商就可以了!在上面的程序结构中,我们需要做的是:
1. 定义一个路虎汽车及其制造商,实现制造车身、引擎、轮胎和组装的方法:
//
Benz汽车
public
class
Benz
...
{ private Body body; private Engine engine; private IList<Wheel> wheels; //…..以下省略 }
public
class
LandRoverBuilder : CarBuilder
...
{ private LandRover car; public override void BuildBody() ...{ car = new Car(); car.AddBody(new Body("business car body")); } public override void BuildEngine() ...{ car.AddEngine(new Engine("benz engine")); } public override void BuildWheel(string name) ...{ car.AddWheel(new Wheel(name)); } //生产汽车,LandRover汽车装配过程 public LandRover GetCar() ...{ //do something like to assemble body and engine together. return car; } }
2. 修改客户端调用,把LandRoverBuilder提供给Producer:
LandRoverBuilder builder
=
new
LandRoverCarBuilder();Producer director
=
new
Producer(builder);director.Construct();LandRover car
=
builder.GetCar();car.ShowMe();
OK,你就得到一辆路虎了!这就是GoF说的
“将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示”的含义!用同样的方式,你甚至可以得到一辆拖拉机!
下面让我们来稍微扩展一下思路,讨论一下扩展问题。我们注意到,在产品这个层次上,每个制造厂直接依赖于具体的汽车,如BenzBuilder生成Benz,在客户端使用时也必须依赖于具体的汽车制造厂BenzBuilder和具体汽车Benz,在抽象制造厂CarBuilder定义中也没有包含GetCar的定义,这是为什么呢?
我们想想把Benz这个汽车这个产品也抽象化,定义为Car,具体汽车Benz和LandRover从Car继承,在CarBuider中定义GetCar方法依赖于抽象Car,从而在客户端也可以依赖于抽象:
CarBuilder builder
=
new
LandRoverCarBuilder(); roducer director
=
new
Producer(builder); irector.Construct(); Car car
=
builder.GetCar(); car.ShowMe();
这种做法没错,你把这个结构图画出来就可以看出,这就成了一个典型的工厂模式!实际上,工厂模式和生成器模式是经常引起混淆和困扰的模式,不过仔细体会两种模式的意图就可以发现,他们关注的重点不同。工厂模式的重点在于产品的系列化的生成问题:工厂方法模式解决一种产品细节差异,抽象工厂模式解决多种产品的系列化创建——这些产品可以很简单,也可以比较复杂。但是,这些产品都是整体创建的。而生成器模式关注的是复杂产品的创建细节——如何一步一步的完成复杂产品部件的创建和最后的组装过程。这个创建复杂产品的过程可以差距巨大,所装配出来的产品也可以差距巨大。比如,上面的CarBuilder定义的抽象过程,只要进行合适的扩充,Producer通过使用具体的生成器就可以生产出小汽车、越野车、赛车、拖拉机,甚至任何由车身、引擎和轮胎可以组合出来的产品!正是由于这些产品之间的差异如此巨大,因此无法在抽象的CarBuilder中定义一个GetProduct之类的抽象方法。
我们再来看Producer这个类。它起到的是指导者的作用,指导生成器的使用方法,也就是利用生成器一步步建造出产品的过程。这个过程一般来说是固定的。通过修改Construct()方法,就可以改变产品的建造过程。当然,如果一个产品的建造过程也是系统的变化因素,当然也可以利用类似工厂模式的方法对Producer进行抽象和封装。
最后我们来讨论一下CarBuilder提供给Producer的方式。生成器模式的核心是给督导者一个生成器,但是具体方式并没有限定。可以像本例这样使用聚合的方式,也可以直接把CarBuilder作为参数提供给Construct方法——这并没有什么本质的区别。
三. 生成器模式角色和结构
生成器模式的一般结构如下图所示:
Builder:抽象生成器角色,这是一个抽象接口,定义了创建负责对象的各个组成部分的公共接口。这个接口应该足够普遍,以便具体的生成器能够通过实现这个抽象接口的不同方式和不同部分生成不同的产品。
ConcreateBuilderA:具体生成器角色,继承并实现抽象生成器的接口,完成具体产品部件的建造。除此以外,具体生成器还需要额外提供一个检索产品的方法,以便指导者能在产品生成后获得它。这个方法通常包含的是复杂产品的装配逻辑。
Director:指导者角色,指导具体生成器来一步一步完成产品的创建。需要特别指出的是,指导者并不需要指导具体生成器生成的产品,它只需要了解如何使用生成器制造产品,制造出的产品的细节,只有具体生成器才知道。
Product:产品,被创建出来的复杂的产品。由ConcreteBuilder创建这个复杂产品的内部表示并定义它的装配过程。通常,Product定义了它的组成部件的类,以及将这些部件装配成最终产品的接口。一个系统中可能还有多个Product类,而且这些类之间不一定会有共同的接口,可以是完全互不相关的。
四. 生成器模式总结
生成器模式用于分步骤构建一个复杂的对象,如何划分步骤是一个稳定的算法,而复杂对象的各个部分的创建过程则经常变化。通过把各部件的创建过程封装到生成器中,使得复杂对象的创建过程和最终表象实现分分离。使用生成器模式,隐藏了具体产品的表示方法、内部结构和装配过程。通过定义一个新的生成器,就可以改变产品的内部表示。
生成器模式中的指导者角色控制着生成器生成部件的过程。因此,通过指导者,可以实现对复杂产品生成步骤进行精细的控制——这一点在复杂产品部件的生成必须遵循一定的次序时显得十分方便。
生成器模式适用于以下场景(GoF):
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
当构造过程必须允许被构造的对象有不同的表示时。