建造者模式是一种较为复杂的创建型模式,它将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端无需知道复杂对象的内部组成部分和装配方式,只需知道所需建造者的类型即可
无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分。
例如汽车:它有车轮、方向盘、发动机等多种部件,对于大部分用户而言,不需知道这些部件的装配细节,而是使用一辆完整的汽车。而如何将这些部件组装成一辆完整的汽车并返回给用户是建造者模式需要解决的问题
学习难度:⭐⭐⭐⭐ 使用频率:⭐⭐
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两个方法,一个是BuilderPartX(),如BuilderPartA、BuilderPartB等,他们用于创建对象的各个部件;另一个方法是GetResult(),他们用于返回复杂对象
它实现Builder接口,实现各个部件的具体构造和装配方法,定义并明确所创建的复杂对象,还可以提供一个方法返回创建好的复杂产品对象
它是被构建的复杂对象,包含多个组件,具体建造者创建该产品的内部表示并定义它的装配过程
它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联,可以在其Constuct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的构建。
客户端一般只需要和指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象,然后通过指挥者类的构造函数或Setter方法将对象传入指挥者中
产品中的复杂对象是指包含多个成员变量的对象,这些成员也称为部件或零件,一个典型的复杂对象示例的代码如下:
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;}
}
}
在抽象建造者类中定义了产品的创建方法和返回方法,典型代码如下:
abstract class Builder
{
//创建产品对象
protected Product product=new Product();
public abstract void BuilderPartA();
public abstract void BuilderPartB();
public abstract void BuilderPartC();
//返回产品对象
public Product GetResult()
{
return product;
}
}
在抽象类Builder中声明了一系列抽象的BuildPartX()方法用于创建复杂对象的各部件,具体建造过程在ConcreteBuilder中实现,此外,还提供一个GetResult()方法用于返回一个已创建好的完整产品对象
在具体建造者中实现了这些BuilderPartX()方法,通过调用Product的Setter方法可以给产品对象的成员变量设值,不同的具体建造者在实现BuilderPartX()方法时将有所区别。典型代码如下:
class ConcreteBuilder:Builder
{
public override void BuilderPartA()
{
product.PartA="A1";
}
public override void BuilderPartB()
{
product.PartB="B1";
}
public override void BuilderPartC()
{
product.PartC="C1";
}
}
指挥者类Director有两个作用:一方面隔离了客户端与创建过程,另一方面控制产品对象的创建过程。指挥者针对抽象者编程,客户端只需知道具体建造者的类型,便可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象。指挥者类的示例代码如下:
class Director
{
private Builder builder;
public Director(Builder builder)
{
this.builder=builder;
}
public void SetBuilder(Builder builder)
{
this.builder=builder;
}
//产品构建与组装方法
public Product Construct()
{
builder.BuilderPartA();
builder.BuilderPartB();
builder.BuilderPartC();
return builder.GetResult();
}
}
在指挥者中可以注入一个抽象建造者类型的对象,它提供一个建造方法Construct(),在该方法中调用builder对象的构造部件的方法,最后返回一个产品对象。
通常客户端只需关心具体建造者的类型,无需关心产品对象的具体组装过程,通常,客户端代码如下:
..
Builder builder =new ConcreteBuilder1();//通过配置文件实现
Director director= new Director(builder);
Product product =director.Construct();
...
用户可以通过配置问加你来存储具体建造者类ConcreteBuilder1的类名,使得在更换新的建造者时无需修改源代码,系统扩展方便。
建造者模式和抽象工厂模式都是较为复杂的创建型模式,创建型模式返回一个完整的复杂产品,而抽象工厂返回一系列相关的产品。在抽象工厂模式中,客户端通过选择具体工厂来生成所需对象,而在建造者模式中,客户端通过指定具体建造者类来指导Director类如何去生成对象,侧重逐步构造一个复杂对象,然后将结果返回
如果抽象工厂模式看成一个汽车配件生产厂,生成不同类型的汽车配件,那么建造者模式就是一个汽车组装厂,通过对配件进行组装返回一辆完整的汽车
实例说明:某游戏软件公司决定开发一款基于角色扮演的多人在线的网络游戏,玩家可以在游戏中扮演虚拟世界中的一个特定角色,角色根据不同的游戏情节和统计数据具有不同的能力,角色也会随着升级而拥有更强大的能力
不同类型的角色,其性别,面容,发型等外观特性有所差异,但是无论是什么造型的角色,它的创建步骤都大同小异,都需要逐步创建其组成部分,再将组成部分装配成一个完整的游戏角色。
现在使用创建者模式来实现游戏角色的创建
Actor:充当复杂产品,游戏角色类
namespace BuilderSample
{
class Actor
{
private string type;
private string sex;
private string face;
private string costume;
private string haircut;
public string Type
{
get{return type;}
set{type=value;}
}
public string Sex
{
get{return sex;}
set{sex=value;}
}
public string Face
{
get{return face;}
set{face=value;}
}
public string Costume
{
get{return costume;}
set{costume=value;}
}
public string Haircut
{
get{return haircut;}
set{haircut=value;}
}
}
}
ActorBuilder:充当抽象创建者,游戏角色建造者
namespace BuilderSample
{
//角色创建者
abstract class ActorBuilder
{
protected Actor actor=new Actor();
public abstract void BuildType();
public abstract void BuildSex();
public abstract void BuildFace();
public abstract void BuildCostume();
public abstract void BuildHaircut();
//工厂方法,返回一个完整的游戏角色对象
public Actor CreateActor()
{
return actor;
}
}
}
HeroBuilder、AngelBuilder、DevilBuilder充当具体建造者
namespace BuilderSample
{
class HeroBuilder: ActorBuilder
{
public override void BuildType()
{
actor.Type="英雄";
}
public override void BuildSex()
{
actor.Sex="男";
}
public override void BuildFace()
{
actor.Face="英俊";
}
public override void BuildCostume()
{
actor.Costume="盔甲";
}
public override void BuildHaircut()
{
actor.Haircut="飘逸";
}
}
}
namespace BuilderSample
{
class AngelBuilder: ActorBuilder
{
public override void BuildType()
{
actor.Type="天使";
}
public override void BuildSex()
{
actor.Sex="女";
}
public override void BuildFace()
{
actor.Face="漂亮";
}
public override void BuildCostume()
{
actor.Costume="白裙";
}
public override void BuildHaircut()
{
actor.Haircut="披肩长发";
}
}
}
namespace BuilderSample
{
class DevilBuilder: ActorBuilder
{
public override void BuildType()
{
actor.Type="恶魔";
}
public override void BuildSex()
{
actor.Sex="妖";
}
public override void BuildFace()
{
actor.Face="丑陋";
}
public override void BuildCostume()
{
actor.Costume="黑衣";
}
public override void BuildHaircut()
{
actor.Haircut="光头";
}
}
}
ActorController充当指挥者
namespace BuilderSample
{
class ActorController
{
//逐步构建复杂产品对象
public Actor Construct(ActorBuilder ab)
{
Actor actor;
ab.BuildType();
ab.BuildSex();
ab.BuildFace();
ab.BuildCostume();
ab.BuildHaircut();
actor=ab.CreateActor();
return actor;
}
}
}
配置文件App.config:在配置文件中存储了具体建造者类的类名
Program:客户端测试类
using System;
using System.Configuration;
using System.Reflection;
namespace BuilderSample
{
class Program
{
static void Main(string[] args)
{
ActorBuilder ab; //针对抽象建造者编程
//读取配置文件
string BuilderType=ConfigurationManager.AppSettings["builder"];
//反射生成对象
ab=(ActorBuilder)Assembly.Load("BuilderSample").CreateInstance(builderType);
ActorController ac=new ActorController();
Actor actor;
actor=ac.Construct(ab); //通过指挥者创建完整的建造者对象
Console.WriteLine("{0}的外观",actor.Type);
Console.WriteLine("性别:{0}",actor.Sex);
Console.WriteLine("面容:{0}",actor.Face);
Console.WriteLine("服饰:{0}",actor.Costume);
Console.WriteLine("发型:{0}",actor.Haircut);
Console.Read();
}
}
}
输出结果如下:
天使的外观:
性别:女
面容:漂亮
服装:白裙
发型:披肩长发
如果需要更换具体角色创建者,只需修改配置文件即可,例如将配置文件中key为“builder”的键值对的value改成"BuilderSample.HeroBuilder",再次运行即可得到:
英雄的外观:
性别:男
面容:英军
服装:盔甲
发型:飘逸
有些情况下,为了简化系统结构,可以将Director和抽象建造者Builder合并,在Builder中提供了逐步构建复杂产品对象的Construct()方法。
如果将游戏角色实例中的指挥者类ActorController省略,ActorBuilder类的代码修改如下:
namespace BuilderSample
{
//角色创建者
abstract class ActorBuilder
{
protected static Actor actor=new Actor();
public abstract void BuildType();
public abstract void BuildSex();
public abstract void BuildFace();
public abstract void BuildCostume();
public abstract void BuildHaircut();
//工厂方法,返回一个完整的游戏角色对象
public static Actor CreateActor(ActorBuilder ab)
{
ab.BuildType();
ab.BuildSex();
ab.BuildFace();
ab.BuildCostume();
ab.BuildHaircut();
return actor;
}
}
}
此时对应的客户端代码也发生修改,代码片段如下:
using System;
using System.Configuration;
using System.Reflection;
namespace BuilderSample
{
class Program
{
static void Main(string[] args)
{
ActorBuilder ab; //针对抽象建造者编程
//读取配置文件
string BuilderType=ConfigurationManager.AppSettings["builder"];
//反射生成对象
ab=(ActorBuilder)Assembly.Load("BuilderExtend").CreateInstance(builderType);
Actor actor;
actor=ActorBuilder.Construct(ab); //通过指挥者创建完整的建造者对象
Console.WriteLine("{0}的外观",actor.Type);
Console.WriteLine("性别:{0}",actor.Sex);
Console.WriteLine("面容:{0}",actor.Face);
Console.WriteLine("服饰:{0}",actor.Costume);
Console.WriteLine("发型:{0}",actor.Haircut);
Console.Read();
}
}
}
除此之外还有一种更简便的方法,可以将Construct()方法中的参数去掉,直接在Construct()方法中调用BuildPartX()方法,代码如下:
namespace BuilderSample
{
//角色创建者
abstract class ActorBuilder
{
protected Actor actor=new Actor();
public abstract void BuildType();
public abstract void BuildSex();
public abstract void BuildFace();
public abstract void BuildCostume();
public abstract void BuildHaircut();
//工厂方法,返回一个完整的游戏角色对象
public Actor CreateActor()
{
this.BuildType();
this.BuildSex();
this.BuildFace();
this.BuildCostume();
this.BuildHaircut();
return actor;
}
}
}
客户端代码如下:
using System;
using System.Configuration;
using System.Reflection;
namespace BuilderSample
{
class Program
{
static void Main(string[] args)
{
ActorBuilder ab; //针对抽象建造者编程
//读取配置文件
string BuilderType=ConfigurationManager.AppSettings["builder"];
//反射生成对象
ab=(ActorBuilder)Assembly.Load("BuilderExtend").CreateInstance(builderType);
Actor actor;
actor=ab.Construct(); //通过指挥者创建完整的建造者对象
Console.WriteLine("{0}的外观",actor.Type);
Console.WriteLine("性别:{0}",actor.Sex);
Console.WriteLine("面容:{0}",actor.Face);
Console.WriteLine("服饰:{0}",actor.Costume);
Console.WriteLine("发型:{0}",actor.Haircut);
Console.Read();
}
}
}
创建者模式除了逐步构建一个复杂产品对象外,还可以通过Director类来更加精细地控制产品地创建过程,例如增加一个类称为钩子方法(Hook Method)的特殊方法来控制是否调用某个BuildPartX()方法
钩子方法的返回类型通常为bool类型,方法名一般为IsXXX(),钩子方法定义在抽象建造者类中。例如可以在游戏角色的抽象建造者类ActorBuilder中定义一个方法IsBareheaded(),用于判断某个角色是否为“光头”,在ActorBuilder中为之提供一个默认实现,返回值为false。代码如下:
namespace BuilderSample
{
//角色创建者
abstract class ActorBuilder
{
protected Actor actor=new Actor();
public abstract void BuildType();
public abstract void BuildSex();
public abstract void BuildFace();
public abstract void BuildCostume();
public abstract void BuildHaircut();
//钩子方法
public virtual bool IsBareheaded()
{
return false;
}
public Actor CreateActor()
{
return actor;
}
}
}
如果某个角色无须构建头发部件,例如“恶魔”,则对应的具体建造者DevilBuilder将覆盖IsBareheaded方法,并将返回值改为true,代码如下:
namespace BuilderSample
{
class DevilBuilder: ActorBuilder
{
public override void BuildType()
{
actor.Type="恶魔";
}
public override void BuildSex()
{
actor.Sex="妖";
}
public override void BuildFace()
{
actor.Face="丑陋";
}
public override void BuildCostume()
{
actor.Costume="黑衣";
}
public override void BuildHaircut()
{
actor.Haircut="光头";
}
//覆盖钩子方法
public override bool IsBareheaded()
{
return true;
}
}
}
同时,指挥者类ActorController的代码修改如下:
namespace BuilderSample
{
class ActorController
{
//逐步构建复杂产品对象
public Actor Construct(ActorBuilder ab)
{
Actor actor;
ab.BuildType();
ab.BuildSex();
ab.BuildFace();
ab.BuildCostume();
//通过钩子方法来控制产品的构建
if(!ab.IsBareheaded())
{
ab.BuildHaircut();
}
actor=ab.CreateActor();
return actor;
}
}
}
当在客户端代码中指定具体建造者类型并通过指挥者来实现产品的逐步构建时,将调用钩子方法IsBareheaded()来判断角色是否有头发,如果为true,将跳过构建发型的方法BuildHairCut(),否则执行它