总目录
前言
在软件系统中,有时需要创建一个复杂对象,并且这个复杂对象由其各部分子对象通过一定的步骤组合而成。例如一个采购系统中,如果需要采购员去采购一批电脑时,在这个实际需求中,电脑就是一个复杂的对象,它是由CPU、主板、硬盘、显卡、机箱等组装而成的,如果此时让采购员一台一台电脑去组装的话真是要累死采购员了,这里就可以采用建造者模式来解决这个问题,我们可以把电脑的各个组件的组装过程封装到一个建造者类对象里,建造者只要负责返还给客户端全部组件都建造完毕的产品对象就可以了。然而现实生活中也是如此的,如果公司要采购一批电脑,此时采购员不可能自己去买各个组件并把它们组织起来,此时采购员只需要像电脑城的老板说自己要采购什么样的电脑就可以了,电脑城老板自然会把组装好的电脑送到公司。下面就以这个例子来展开建造者模式的介绍。
1 基本介绍
建造者模式(Builder Pattern),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。说白了就是将一个复杂的对象拆分成一个一个零件,然后按照既定顺序和规则进行组装,最终形成这个相对复杂的对象。
建造者模式的本质是使组装过程(用指挥者类进行封装,从而达到解耦的目的)和创建具体产品解耦,使我们不用去关心每个组件是如何组装的。
建造者模式中的4个角色
- Product(产品):复杂对象本身。
- Builder(抽象建造者):既可以是抽象类也可以是接口,主要是为了约束和规范具体建造者有哪些建造行为,并提供一个方法返回组装后的复杂对象。
- ConcreteBuilder(具体建造者):它继承自Builder(抽象建造者),主要是具体实现父类中的那些建造行为。也就是说在这个类里就要实际去创建各个零件的具体功能了。
- Director(指挥者):又称为导演类,在指挥者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象,然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。
2 使用场景
有时需要创建一个复杂对象,其通常由其各部分子对象通过一定的步骤组合而成。
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,并不适合使用建造者模式。
本质就是:创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。使得相同的创建过程可以创建不同的产品。
造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式。
垃圾食品套餐系统:汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出各种特惠"套餐"。
装修系统:改水电、再贴瓷砖、家电、电视墙等,顺序基本不变,用材不同最终生成的产品则不同。
…
3 实现方式
在这个例子中,电脑城的老板是直接与客户(也就是指采购员)联系的,然而电脑的组装是由老板指挥装机人员去把电脑的各个部件(CPU,风扇…等)组装起来,真正负责创建产品(这里产品指的就是电脑)的人就是电脑城的装机人员。
- Computer是产品,包含CPU、主板、内存、硬盘、显卡、机箱等组件。
- Builder是抽象建造者,
- XiaoWangBuilder和XiaoZhangBuilder是具体的建造者,也就是装机人员
- Director是指挥者,也就是电脑城的老板。
1 首先要明确,产品类以及产品类的组件:
//产品类
public class Computer
{
public string CPU { get; set; }
public string Memory { get; set; }
//其他的组件 省略
public string Disk { get; set; }
}
2 再者,抽象的建造类,将建造者的建造行为抽象出来了,并提供一个方法返回组装后复杂产品
//抽象建造者类
public abstract class AbstactComputerBuilder
{
protected Computer computer = new Computer();
//以下方法定义了产品不同组件的建造行为
public abstract void BuildCPU(string cpu);
public abstract void BuildMemory(string Memory);
public abstract void BuildDisk(string disk);
//定义一个方法返回产品实例
public Computer GetComputer()
{
Console.WriteLine("电脑组装完成!");
return computer;
}
}
3 然后,是具体的建造者类,负责具体的建造工作
//装机小哥 张三
public class ZhangSanBuilder : AbstactComputerBuilder
{
public override void BuildCPU(string cpu)
{
computer.CPU = cpu;
Console.WriteLine($"张三已将CPU:{computer.CPU}组装完成");
}
public override void BuildDisk(string disk)
{
computer.Disk =disk;
Console.WriteLine($"张三已将硬盘:{computer.Disk}组装完成");
}
public override void BuildMemory(string memory)
{
computer.Memory = memory;
Console.WriteLine($"张三已将内存:{computer.Memory}组装完成");
}
}
//装机小哥 李四
public class LiSiBuilder : AbstactComputerBuilder
{
public override void BuildCPU(string cpu)
{
computer.CPU = cpu;
Console.WriteLine($"李四已将CPU:{computer.CPU}组装完成");
}
public override void BuildDisk(string disk)
{
computer.Disk = disk;
Console.WriteLine($"李四已将硬盘:{computer.Disk}组装完成");
}
public override void BuildMemory(string memory)
{
computer.Memory = memory;
Console.WriteLine($"李四已将内存:{computer.Memory}组装完成");
}
}
4 指挥者,指挥装机小哥装机并制定和产品对象的建造顺序
public class Director
{
private AbstactComputerBuilder _builder = null;
//通过构造函数传递具体创造者
public Director(AbstactComputerBuilder builder)
{
this._builder = builder;
}
// 组装方法,并返回产品
public Computer AssembleComputer(string cpu,string disk,string memory)
{
_builder.BuildCPU(cpu);
_builder.BuildDisk(disk);
_builder.BuildMemory(memory);
return _builder.GetComputer();
}
}
在上述代码中是通过构造函数传递具体建造者,也可以在AssembleComputer方法中传递。产品的具体组装顺序则是由AssembleComputer方法来完成,如果有多种组装方式,也可以有多个方法来分别完成。该类本质就是统筹安排,并直接与客户端进行交互。
5 最后,客户端的调用
public static void Main(string[] args)
{
Console.WriteLine("我想组装一台工作电脑:");
//首先实例化一个具体建造者对象:装机小哥 张三
AbstactComputerBuilder zhangSan = new ZhangSanBuilder();
//然后使用指挥者类来具体组装这个产品
Computer workComputer = new Director(zhangSan).AssembleComputer("I5","500G","6G");
Console.WriteLine("\n我又想组装一台游戏电脑:");
//实例化一个具体建造者对象:装机小哥 李四
AbstactComputerBuilder liSi = new LiSiBuilder();
//然后使用指挥者类来具体组装这个产品
Computer gameComputer = new Director(liSi).AssembleComputer("I7","2T","16G");
Console.ReadLine();
}
6 从上面我们发现,指挥者主要的作用就是制定组装的流程,如果我们去掉将制定的过程交给建造者抽象类呢
//抽象建造者类
public abstract class AbstactComputerBuilder
{
protected Computer computer = new Computer();
//以下方法定义了产品不同组件的建造行为
public abstract void BuildCPU(string cpu);
public abstract void BuildMemory(string Memory);
public abstract void BuildDisk(string disk);
// 组装方法,并返回产品
public Computer AssembleComputer(string cpu, string disk, string memory)
{
this.BuildCPU(cpu);
this.BuildDisk(disk);
this.BuildMemory(memory);
return this.GetComputer();
}
//定义一个方法返回产品实例
public Computer GetComputer()
{
Console.WriteLine("电脑组装完成!");
return computer;
}
}
此时的客户端调用:
public static void Main(string[] args)
{
Console.WriteLine("我想组装一台工作电脑:");
//首先实例化一个具体建造者对象:装机小哥 张三
AbstactComputerBuilder zhangSan = new ZhangSanBuilder();
//然后使用指挥者类来具体组装这个产品
Computer workComputer = zhangSan.AssembleComputer("I5","500G","6G");
Console.WriteLine("\n我又想组装一台游戏电脑:");
//实例化一个具体建造者对象:装机小哥 李四
AbstactComputerBuilder liSi = new LiSiBuilder();
//然后使用指挥者类来具体组装这个产品
Computer gameComputer = liSi.AssembleComputer("I7","2T","16G");
Console.ReadLine();
}
4 相关分析
介绍完了建造者模式的具体实现之后,让我们总结下建造模式的实现要点:
在建造者模式中,指挥者是直接与客户端打交道的,指挥者将客户端创建产品的请求划分为对各个部件的建造请求,再将这些请求委派到具体建造者角色,具体建造者角色是完成具体产品的构建工作的,却不为客户所知道。
建造者模式主要用于“分步骤来构建一个复杂的对象”,其中“分步骤”是一个固定的组合过程,而复杂对象的各个部分是经常变化的(也就是说电脑的内部组件是经常变化的,这里指的的变化如硬盘的大小变了,CPU由单核变双核等)。
产品不需要抽象类,由于建造模式的创建出来的最终产品可能差异很大,所以不大可能提炼出一个抽象产品类。
由于建造者隐藏了具体产品的组装过程,所以要改变一个产品的内部表示,只需要再实现一个具体的建造者就可以了,从而能很好地应对产品组成组件的需求变化。
5 创建者模式与其他工厂模式的对比
- 工厂模式解决了“同一类产品”的需求变化,抽象工厂模式解决了“系列产品”的需求变化,而建造者模式解决的是 “产品部分” 的需要变化。
- 工厂方法模式 VS 建造者模式
- 工厂方法模式侧重整体对象的创建方式,建造者模式侧重零部件的构建,然后通过一定顺序和规则构造出一个复杂的对象。例如:想要制作一个假人,如果使用工厂方法模式,直接生产出来一个XX材质、XX高、XX重的假人人就可以了。而如果使用建造者模式,则需要先创建出四肢、头和躯干等部位,然后按照一定顺序进行组装形成一个完整的假人。
- 抽象工厂模式 VS 建造者模式
- 抽象工厂模式侧重对产品簇(系列产品)的创建,一个产品簇是这样的一系列产品。
- 采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。
- 建造者模式则侧重要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。
结语
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料:
c#建造者模式详解