建造者模式的定义:
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式的结构:
(1)Builder(抽象建造者):它为创建一个产品Product对象的各个部件制定抽象接口。在该接口中有两类方法:一类是BuilPartA()、BuildPartB()等这样的抽象方法,用于创建Product对象的各个部件,另一类方法是GetResult(),用于返回创建好的Product对象。Builder既可以是接口也可以是抽象类。
(2)ConcreteBuilder(具体建造者):它实现了Builder接口中的抽象方法,也就是说它实现了Product对象创建的具体过程,还可以在这里提供一个方法用于返回创建好的Product对象
(3)Product(产品):它是被构建的复杂的对象,包含多个组件。
(4)Director(指挥者):指挥者又称指导类,它负责安排复杂对象的建造次序。客户端一般只和指挥者来进行交互,客户端指定具体建造的类型,并实例化具体建造者(可以通过配置和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。
建造者模式的实现:
class Product:
public class Product { private string _partA; private string _partB; private string _partC; public string PartA { get { return _partA; } set { _partA = value; } } public string PartB { get { return _partB; } set { _partB = value; } } public string PartC { get { return _partC; } set { _partC = value; } } }
class Builder:
public abstract class Builder { //创建产品对象 protected Product product; public Builder() { product = new Product(); } public abstract void BuilderPartA(); public abstract void BuilderPartB(); public abstract void BuilderPartC(); /// <summary> /// 返回产品对象 /// </summary> /// <returns></returns> public Product GetResult() { return product; } }
class ConcreteBuilder1:
public class ConcreteBuilder1 : Builder { public override void BuilderPartA() { product.PartA = "A1"; } public override void BuilderPartB() { product.PartB = "B1"; } public override void BuilderPartC() { product.PartC = "C1"; } }
class Director:
public class Director { private Builder builder; public Director (Builder builder) { this.builder = builder; } public void SetBuilder(Builder builder) { this.builder = builder; }
//产品的构建与组装方法 public Product Contruct() { builder.BuilderPartA(); builder.BuilderPartB(); builder.BuilderPartC(); return builder.GetResult(); } }
对于客户端而言,只需要关心具体建造者的类型,无需关心产品对象的具体组装过程。客户端代码:
Builder builder = new ConcreteBuilder1();//可通过配置文件实现 Director director = new Director(builder); Product product = director.Contruct();
建造者模式的应用:
某公司开发一款基于角色扮演的多人在线网络游戏,角色作为该游戏的一个重要的组成的部分,需要对游戏角色进行设计,通过分析发现,游戏角色是一个复杂的对象,它包含性别,面容等,不容类型的游戏角色,其性别,面容,服装,发型等特性都有所差异。现在使用建造者模式来实现游戏角色的创建。
代码:
Actor:
/// <summary> /// 游戏角色类,充当复杂产品对象。 /// </summary> public class Actor { public string Type { get; set; } public string Sex { get; set; } public string Face { get; set; } public string Contume { get; set; } public string HairStype { get; set; } }
ActorBuilder:
/// <summary> /// 角色创建者:抽象建造者 /// </summary> public abstract class ActorBuilder { protected Actor _actor; public ActorBuilder() { this._actor = new Actor(); } public abstract void BuildType(); public abstract void BuildSex(); public abstract void BuilFace(); public abstract void BuildCostume(); public abstract void BuildHairStyle(); //工厂方法,返回一个完整的游戏角色对象 public Actor CreateActor() { return this._actor; } }
HeroBuilder:
/// <summary> /// 英雄角色建造者,充当具体建造者 /// </summary> public class HeroBuilder : ActorBuilder { public override void BuildCostume() { _actor.Contume = "盔甲"; } public override void BuildHairStyle() { _actor.HairStype = "飘逸"; } public override void BuildSex() { _actor.Sex = "男"; } public override void BuildType() { _actor.Type = "英雄"; } public override void BuilFace() { _actor.Face = "英俊"; } }
AngelBuilder:
/// <summary> /// 天使角色建造者,充当具体建造者 /// </summary> public class AngelBuilder : ActorBuilder { public override void BuildCostume() { _actor.Contume = "白裙"; } public override void BuildHairStyle() { _actor.HairStype = "披肩长发"; } public override void BuildSex() { _actor.Sex = "女"; } public override void BuildType() { _actor.Type = "天使"; } public override void BuilFace() { _actor.Face = "飘亮"; } }
DevilBuilder:
/// <summary> /// 恶魔角色建造者,充当具体建造者 /// </summary> public class DevilBuilder:ActorBuilder { public override void BuildCostume() { _actor.Contume = "黑衣"; } public override void BuildHairStyle() { _actor.HairStype = "光头"; } public override void BuildSex() { _actor.Sex = "男"; } public override void BuildType() { _actor.Type = "恶魔"; } public override void BuilFace() { _actor.Face = "丑陋"; } }
AcotrController:
/// <summary> /// 角色控制器,充当指挥者 /// </summary> public class ActorController { public Actor Consttruct(ActorBuilder ab) { Actor actor; ab.BuildType(); ab.BuildSex(); ab.BuilFace(); ab.BuildCostume(); ab.BuildHairStyle(); actor = ab.CreateActor(); return actor; }
配置文件App.config:
<appSettings> <add key="builder" value="Model.BuilderPattern.Test.AngelBuilder"/> </appSettings>
客户端代码:
static void Main(string[] args) { //针对抽象建造者编程 ActorBuilder ab; //读取配置 string buildType = ConfigurationManager.AppSettings["builder"]; //反射生成 ab = (ActorBuilder)Assembly.Load("Model.BuilderPattern").CreateInstance(buildType); ActorController ac = new ActorController(); Actor actor; //通过指挥者创建完整的建造者对象 actor = ac.Consttruct(ab); System.Console.WriteLine("{0}的外观:", actor.Type); System.Console.WriteLine("性别:{0}", actor.Sex); System.Console.WriteLine("面容:{0}", actor.Face); System.Console.WriteLine("服装:{0}", actor.Contume); System.Console.WriteLine("发型:{0}", actor.HairStype); System.Console.ReadKey(); //运行结果: //天使的外观: //性别:女 //面容:飘亮 //服装:白裙 //发型:披肩长发 }
结果分析:如果需要生成英雄的外观,只需要更改配置value="Model.BuilderPattern.Test.HeroBuilder"即可。当需要添加新的角色的时候只需要将新的具体角色建造者作为抽象角色建造者的子类,然后修改配置文件即可,原有代码无需修改,完全复合开闭原则。
有时候我们为了简化系统结构可能会省略Director,也就是将游戏角色实例中的指挥者类ActorController省略。主要修改ActorBuilder和客户端代码,代码修改如下:
class ActorBuilder:
/// <summary> /// 角色创建者:抽象建造者 /// </summary> public abstract class ActorBuilder { protected Actor _actor; public ActorBuilder() { this._actor = new Actor(); } public abstract void BuildType(); public abstract void BuildSex(); public abstract void BuilFace(); public abstract void BuildCostume(); public abstract void BuildHairStyle(); public Actor Consttruct() { this.BuildType(); this.BuildSex(); this.BuilFace(); this.BuildCostume(); this.BuildHairStyle(); return _actor; } }
客户端代码:
static void Main(string[] args) { //针对抽象建造者编程 ActorBuilder ab; //读取配置 string buildType = ConfigurationManager.AppSettings["builder"]; //反射生成 ab = (ActorBuilder)Assembly.Load("Model.BuilderPattern").CreateInstance(buildType); Actor actor; //通过指挥者创建完整的建造者对象 actor = ab.Consttruct(); System.Console.WriteLine("{0}的外观:", actor.Type); System.Console.WriteLine("性别:{0}", actor.Sex); System.Console.WriteLine("面容:{0}", actor.Face); System.Console.WriteLine("服装:{0}", actor.Contume); System.Console.WriteLine("发型:{0}", actor.HairStype); System.Console.ReadKey(); //运行结果: //天使的外观: //性别:女 //面容:飘亮 //服装:白裙 //发型:披肩长发 } }
其他的不改变,以上对Director类的省略方法都不影响系统的灵活性和可扩展性,同时还简化了系统结构,但加重了抽象建造者类的职责,如果Construct()方法较为复杂,待构建产品组成部分较多,建议将Construct()方法单独封装到Director中,这样更符合单一职责原则。
2:钩子方法的引入
建造者模式除了逐步构件一个复杂产品对象外,还可以通过Director类来更加惊喜地空值产品的创建过程。钩子方法的返回类型通常为bool类型,方法名一般为Isxxx(),钩子方法定义在抽象建造者类中。例如可以在游戏角色的抽象建造者类ActorBuilder中定一个方法IsBareheaded(),用于判断某个角色是否为光头(Bareheaded),在ActorBuilder中为之提供一个默认实现,其返回值为false,代码如下:
ActorBuilder:
/// <summary> /// 角色创建者:抽象建造者 /// </summary> public abstract class ActorBuilder { protected Actor _actor; public ActorBuilder() { this._actor = new Actor(); } public abstract void BuildType(); public abstract void BuildSex(); public abstract void BuilFace(); public abstract void BuildCostume(); public abstract void BuildHairStyle(); //钩子方法,需要使用virtual关键字 public virtual bool IsBareheaded() { return false; } public Actor Consttruct() { this.BuildType(); this.BuildSex(); this.BuilFace(); this.BuildCostume(); this.BuildHairStyle(); return _actor; } }
DevilBuilder:
/// <summary> /// 恶魔角色建造者,充当具体建造者 /// </summary> public class DevilBuilder:ActorBuilder { public override void BuildCostume() { _actor.Contume = "黑衣"; } public override void BuildHairStyle() { _actor.HairStype = "光头"; } public override void BuildSex() { _actor.Sex = "男"; } public override void BuildType() { _actor.Type = "恶魔"; } public override void BuilFace() { _actor.Face = "丑陋"; } //覆盖钩子方法 public override bool IsBareheaded() { return true; } }
同时指挥者类ActorController的代码如下:
/// <summary> /// 角色控制器,充当指挥者 /// </summary> public class ActorController { public Actor Consttruct(ActorBuilder ab) { Actor actor; ab.BuildType(); ab.BuildSex(); ab.BuilFace(); ab.BuildCostume(); //通过钩子方法来控制产品的构建 if (!ab.IsBareheaded()) { ab.BuildHairStyle(); } actor = ab.CreateActor(); return actor; } }
通过引入钩子方法,可以在Director中对复杂产品的构建进行精细的控制,不仅指定BuilderPartX()方法的执行顺序,还可以控制是否需要执行某个BuilderPartX()方法。
建造者模式的优缺点:
建造者模式优点如下:
(1)在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建进行解耦,使得相同的创建过程可以创建不同的产品对象
(2)每一个具体建造者都是独立的,与其他的具体建造者无关,因此可以很方便的替换具体建造者或增加具体建造者用户使用不同的具体建造者即可得到不同的产品对象,由于指挥者类针对抽象建造者编程,增加新的具体建造者无需修改原有类库的代码,系统扩展方便,符合开闭原则。
(3)用户可以更加精细的空值产品的创建过程,将复杂的产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
建造者模式缺点如下:
(1)建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性较大,例如很多组成部分不相同,则不适合使用建造者模式,因此使用范围收到一定的限制
(2)如果产品的内部变化复杂,可能会需要定义很多具体建造者类来实现这种变化,导致系统变得和庞大,增加了系统的理解难度和运行成本。
建造者模式的使用环境:
(1)需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量。
(2)需要生成的产品对象的属性互相依赖,需要指定生成的顺序。
(3)对象的创建过程独立于创建该对象的类。在建造者模式中通过引入指挥者类,将创建对象过程封装在指挥者类中,而不再建造者类和客户类中。
(4)隔离复杂对象的创建和使用,并使得相同的创建过程创建不同的产品。