5. 生成器模式
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
《设计模式:可复用面向对象软件的基础》
如果说之前提及的工程模式、抽象工厂模式和单件模式都是在创建对象的话,那么生成器模式就是一个“精工细作”的工匠,因为它用于创建复杂对象,从独立创建对象的每个部分到最后的组装,它承担每个步骤的工作。
由于它把创建每个部分都独立为一个单一的过程,因此不仅可以完成较为“精细”的创建,还可以编排创建步骤,生产不同的目标实例。
经典模式
生成器模式解决的问题是产品局部加工过程变化较大,但组装过程相对固定。例如:
- 组装PC机,虽然声卡、显卡、内存、硬盘、机箱、CPU等有很多不同,每个部分的制造和生产过程都不同。但无论是品牌机还是个人攒机,组装一台计算机的过程相对固定。
- 汽车的生产与此类似,虽然不同品牌、不同档次的汽车在轮胎、发动机、车身的制作工艺上有很大区别,但组装的过程相对而言非常稳定。
— 类图在此 —
public class Car
{
public Car()
{
StepSequence = new List<string>();
}
public Wheel Wheel { get; set; }
public Engine Engine { get; set; }
public Body Body { get; set; }
public IList<string> StepSequence { get; private set; }
}
public interface IBuilder
{
void BuildBody();
void BuildEngine();
void BuildWheel();
}
public abstract class BuilderBase : IBuilder
{
protected Car _Car;
public BuilderBase()
{
_Car = new Car();
}
public Car GetProduct()
{
return _Car;
}
public virtual void BuildBody()
{
_Car.StepSequence.Add("Body");
}
public virtual void BuildEngine()
{
_Car.StepSequence.Add("Engine");
}
public virtual void BuildWheel()
{
_Car.StepSequence.Add("Wheel");
}
}
public class CarBuilder : BuilderBase
{
public override void BuildBody()
{
base.BuildBody();
_Car.Body = new Body();
}
public override void BuildEngine()
{
base.BuildEngine();
_Car.Engine = new Engine();
}
public override void BuildWheel()
{
base.BuildWheel();
_Car.Wheel = new Wheel();
}
}
public class AdvanceCarBuilder : BuilderBase
{
public override void BuildBody()
{
base.BuildBody();
_Car.Body = new AdvancedBody();
}
public override void BuildEngine()
{
base.BuildEngine();
_Car.Engine = new AdvancedEngine();
}
public override void BuildWheel()
{
base.BuildWheel();
_Car.Wheel = new AdvancedWheel();
}
}
public class SUVBuilder : BuilderBase
{
public override void BuildBody()
{
base.BuildBody();
_Car.Body = new SUVBody();
}
public override void BuildEngine()
{
base.BuildEngine();
_Car.Engine = new SUVEngine();
}
public override void BuildWheel()
{
base.BuildWheel();
_Car.Wheel = new SUVWheel();
}
}
public class Director
{
public void BuildCar(IBuilder builder)
{
builder.BuildBody();
builder.BuildEngine();
builder.BuildWheel();
}
}
参与者:
- Builder(IBuilder):负责描述创建一个产品各个组成的抽象接口。
- Concrete Builder(CarBuilder, AdvancedBuilder, SUVBuilder):实现Builder要求的内容,并且提供一个获得产品的方法。
- Director:虽然Builder定义了构造产品的每个步骤,但是Director告诉如何借助Builder生产产品的过程,它对Builder的操作完全基于Builder的抽象方法。
最终在使用时需要将三者结合起来,构建一个复杂的对象。
[TestClass]
public class TestBuilder
{
private Director _CarDirector = new Director();
[TestMethod]
public void Test_CarBuilder()
{
CarBuilder builder = new CarBuilder();
_CarDirector.BuildCar(builder);
Car car = builder.GetProduct();
Assert.IsNotNull(car);
Assert.AreEqual("Normal", car.Engine.Name);
Assert.AreEqual(27, car.Engine.Power);
Assert.AreEqual("Normal", car.Body.Name);
Assert.AreEqual(27, car.Body.Size);
Assert.AreEqual("Normal", car.Wheel.Name);
Assert.AreEqual(27, car.Wheel.Size);
}
[TestMethod]
public void Test_AdvancedCarBuilder()
{
AdvanceCarBuilder builder = new AdvanceCarBuilder();
_CarDirector.BuildCar(builder);
Car car = builder.GetProduct();
Assert.IsNotNull(car);
Assert.AreEqual("Advanced", car.Engine.Name);
Assert.AreEqual(43, car.Engine.Power);
Assert.AreEqual("Advanced", car.Body.Name);
Assert.AreEqual(28, car.Body.Size);
Assert.AreEqual("Advanced", car.Wheel.Name);
Assert.AreEqual(28, car.Wheel.Size);
}
[TestMethod]
public void Test_SUVBuilder()
{
SUVBuilder builder = new SUVBuilder();
_CarDirector.BuildCar(builder);
Car car = builder.GetProduct();
Assert.IsNotNull(car);
Assert.AreEqual("SUV", car.Engine.Name);
Assert.AreEqual(60, car.Engine.Power);
Assert.AreEqual("SUV", car.Body.Name);
Assert.AreEqual(30, car.Body.Size);
Assert.AreEqual("SUV", car.Wheel.Name);
Assert.AreEqual(30, car.Wheel.Size);
Assert.AreEqual(3, car.StepSequence.Count);
Assert.AreEqual("Body", car.StepSequence[0]);
Assert.AreEqual("Engine", car.StepSequence[1]);
Assert.AreEqual("Wheel", car.StepSequence[2]);
}
}
优点:
- 生成器模式将复杂对象的每个组成步骤暴露出来,借助Director既可以选择其执行次序,也可以选择要执行的步骤,比起工厂模式和抽象工厂模式,生成器模式更适合创建“更为复杂且每个组成部分变化较多”的类型。
- 向客户程序屏蔽了对象创建过程中的多变性。
缺点:
- 生成器模式会暴露出更多的执行步骤,需要Director具有更多的领域知识,使用不慎很容易造成紧密的耦合。
扩展
回想一下,现实中我们组装一台机器或组装一部汽车的时候,经常采取的方法是检查产品自己的说明书,然后开始执行组装工作。对于待加工的对象是否也可以考虑采取类似的方法呢?
看说明书加工的生成器
.NET平台有个很好的机制——“贴标签”,使用Attribute来表示扩展目标产品类型的创建信息,这样我们可以创建一个非常通用的Director类,由它“看着说明书”加工多种多样的产品。
— 类图在此 —
[AttributeUsage(AttributeTargets.Class)]
public class InstructionAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class BuildStepAttribute : Attribute, IComparable<BuildStepAttribute>
{
public int Sequence { get; private set; }
public int Times { get; private set; }
public BuildStepAttribute(int seuqence, int times = 1)
{
Sequence = seuqence;
Times = times;
}
public int CompareTo(object target)
{
if (target == null || !(target is BuildStepAttribute))
throw new ArgumentException("target");
return this.CompareTo(target as BuildStepAttribute);
}
public int CompareTo(BuildStepAttribute target)
{
if (target == null)
throw new ArgumentException("target");
return this.Sequence - target.Sequence;
}
}
[Instruction]
public class SUVBuilder : BuilderBase
{
[BuildStep(1)]
public override void BuildBody()
{
base.BuildBody();
_Car.Body = new SUVBody();
}
[BuildStep(3)]
public override void BuildEngine()
{
base.BuildEngine();
_Car.Engine = new SUVEngine();
}
[BuildStep(2)]
public override void BuildWheel()
{
base.BuildWheel();
_Car.Wheel = new SUVWheel();
}
}
public class Director<T>
where T : class
{
public void BuildUpByDefine(T builder)
{
if (builder == null) return;
IEnumerable<MethodInfo> methods = this.GetInterfaceMethods(builder);
Type builderType = builder.GetType();
InstructionAttribute iAttribute = builderType.GetCustomAttribute<InstructionAttribute>();
if (iAttribute != null)
methods = this.SortMethodByStep(methods);
foreach (MethodInfo m in methods)
m.Invoke(builder, null);
}
private IEnumerable<MethodInfo> GetInterfaceMethods(T builder)
{
List<MethodInfo> methods = new List<MethodInfo>();
Type builderType = builder.GetType();
MethodInfo[] typeMethods = builderType.GetMethods(BindingFlags.Instance | BindingFlags.Public);
Type interfaceType = typeof(T);
MethodInfo[] interfaceMethods = interfaceType.GetMethods();
foreach (MethodInfo im in interfaceMethods)
{
MethodInfo fm = typeMethods.First(m => m.Name == im.Name);
if (fm == null) continue;
methods.Add(fm);
}
return methods;
}
private IEnumerable<MethodInfo> SortMethodByStep(IEnumerable<MethodInfo> methods)
{
if (methods.Count() <= 0) return methods;
Dictionary<BuildStepAttribute, MethodInfo> executeMethods =
new Dictionary<BuildStepAttribute, MethodInfo>();
foreach (MethodInfo m in methods)
{
BuildStepAttribute attribute = m.GetCustomAttribute<BuildStepAttribute>();
if (attribute == null) continue;
executeMethods.Add(attribute, m);
}
if (executeMethods.Count <= 0) return new List<MethodInfo>();
return executeMethods.OrderBy(p => p.Key).Select(p => p.Value);
}
public virtual void TearDown() { }
}
Unit Test:
[TestMethod]
public void Test_GenericDirector()
{
SUVBuilder builder = new SUVBuilder();
Director<IBuilder> director = new Director<IBuilder>();
director.BuildUpByDefine(builder);
Car car = builder.GetProduct();
Assert.IsNotNull(car);
Assert.AreEqual("SUV", car.Engine.Name);
Assert.AreEqual(60, car.Engine.Power);
Assert.AreEqual("SUV", car.Body.Name);
Assert.AreEqual(30, car.Body.Size);
Assert.AreEqual("SUV", car.Wheel.Name);
Assert.AreEqual(30, car.Wheel.Size);
Assert.AreEqual(3, car.StepSequence.Count);
Assert.AreEqual("Body", car.StepSequence[0]);
Assert.AreEqual("Wheel", car.StepSequence[1]);
Assert.AreEqual("Engine", car.StepSequence[2]);
}
[TestMethod]
public void Test_GenericDirector_2()
{
CarBuilder builder = new CarBuilder();
Director<IBuilder> director = new Director<IBuilder>();
director.BuildUpByDefine(builder);
Car car = builder.GetProduct();
Assert.IsNotNull(car);
Assert.AreEqual("Normal", car.Engine.Name);
Assert.AreEqual(27, car.Engine.Power);
Assert.AreEqual("Normal", car.Body.Name);
Assert.AreEqual(27, car.Body.Size);
Assert.AreEqual("Normal", car.Wheel.Name);
Assert.AreEqual(27, car.Wheel.Size);
Assert.AreEqual(3, car.StepSequence.Count);
Assert.AreEqual("Body", car.StepSequence[0]);
Assert.AreEqual("Engine", car.StepSequence[1]);
Assert.AreEqual("Wheel", car.StepSequence[2]);
}
这个通用的Director可以继续扩展,做出更多的定制。
public class CarDirector : Director<IBuilder>
{
public void BuildUp(IBuilder builder)
{
builder.BuildWheel();
builder.BuildEngine();
builder.BuildBody();
}
}
Unit Test:
[TestMethod]
public void Test_CarDirector()
{
// BuildUpByDefine
SUVBuilder builder = new SUVBuilder();
CarDirector director = new CarDirector();
director.BuildUpByDefine(builder);
Car car = builder.GetProduct();
Assert.IsNotNull(car);
Assert.AreEqual("SUV", car.Engine.Name);
Assert.AreEqual(60, car.Engine.Power);
Assert.AreEqual("SUV", car.Body.Name);
Assert.AreEqual(30, car.Body.Size);
Assert.AreEqual("SUV", car.Wheel.Name);
Assert.AreEqual(30, car.Wheel.Size);
Assert.AreEqual(3, car.StepSequence.Count);
Assert.AreEqual("Body", car.StepSequence[0]);
Assert.AreEqual("Wheel", car.StepSequence[1]);
Assert.AreEqual("Engine", car.StepSequence[2]);
// BuildUp
builder = new SUVBuilder();
director = new CarDirector();
director.BuildUp(builder);
car = builder.GetProduct();
Assert.IsNotNull(car);
Assert.AreEqual("SUV", car.Engine.Name);
Assert.AreEqual(60, car.Engine.Power);
Assert.AreEqual("SUV", car.Body.Name);
Assert.AreEqual(30, car.Body.Size);
Assert.AreEqual("SUV", car.Wheel.Name);
Assert.AreEqual(30, car.Wheel.Size);
Assert.AreEqual(3, car.StepSequence.Count);
Assert.AreEqual("Wheel", car.StepSequence[0]);
Assert.AreEqual("Engine", car.StepSequence[1]);
Assert.AreEqual("Body", car.StepSequence[2]);
}
思考:
从整个使用过程来看,应用程序需要指定Concrete Builder,这点与创建型模式延迟实例化的思路是不一致的。可以考虑将工厂模式、抽象工厂模式与生成器模式结合使用。