无废话C#设计模式之六:Builder
意图
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
场景
在电脑城装机总有这样的经历。我们到了店里,先会有一个销售人员来询问你希望装的机器是怎么样的配置,他会给你一些建议,最终会形成一张装机单。和客户确定了装机配置以后,他会把这张单字交给提货的人,由他来准备这些配件,准备完成后交给装机技术人员。技术人员会把这些配件装成一个整机交给客户。
不管是什么电脑,它总是由CPU、内存、主板、硬盘以及显卡等部件构成的,并且装机的过程总是固定的:
l 把主板固定在机箱中
l 把CPU安装到主板上
l 把内存安装到主板上
l 把硬盘连接到主板上
l 把显卡安装到主板上
但是,每台兼容机的部件都各不相同的,有些配置高一点,有些配置低一点,这是变化点。对于装机技术人员来说,他不需要考虑这些配件从哪里来的,他只需要把他们组装在一起了,这是稳定的装机流程。要把这种变化的配件和稳定的流程进行分离就需要引入Builder模式。
示例代码
using System; using System.Collections.Generic; using System.Text; using System.Reflection; namespace BuilderExemple { classProgram { staticvoid Main(string[] args) { ComputerFactory factory = newComputerFactory(); ComputerBuilder office = newOfficeComputerBuilder(); factory.BuildComputer(office); office.Computer.ShowSystemInfo(); ComputerBuilder game = newGameComputerBuilder(); factory.BuildComputer(game); game.Computer.ShowSystemInfo(); } } classComputerFactory { publicvoid BuildComputer(ComputerBuilder cb) { Console.WriteLine(); Console.WriteLine(">>>>>>>>>>>>>>>>>>Start Building " + cb.Name); cb.SetupMainboard(); cb.SetupCpu(); cb.SetupMemory(); cb.SetupHarddisk(); cb.SetupVideocard(); Console.WriteLine(">>>>>>>>>>>>>>>>>>Build " + cb.Name + " Completed"); Console.WriteLine(); } } abstractclassComputerBuilder { protectedstring name; publicstring Name { get { return name; } set { name = value; } } protectedComputer computer; publicComputer Computer { get { return computer; } set { computer = value; } } public ComputerBuilder() { computer = newComputer(); } publicabstractvoid SetupMainboard(); publicabstractvoid SetupCpu(); publicabstractvoid SetupMemory(); publicabstractvoid SetupHarddisk(); publicabstractvoid SetupVideocard(); } classOfficeComputerBuilder : ComputerBuilder { public OfficeComputerBuilder() { name = "OfficeComputer"; } publicoverridevoid SetupMainboard() { computer.Mainboard = "Abit升技LG-95C 主板(Intel 945GC芯片组/LGA 775/1066MHz) "; } publicoverridevoid SetupCpu() { computer.Cpu = "Intel 英特尔赛扬D 336 (2.8GHz/LGA 775/256K/533MHz) "; } publicoverridevoid SetupMemory() { computer.Memory = "Patriot博帝DDR2 667 512MB 台式机内存"; } publicoverridevoid SetupHarddisk() { computer.Harddisk = "Hitachi日立SATAII接口台式机硬盘(80G/7200转/8M)盒装"; } publicoverridevoid SetupVideocard() { computer.Videocard = "主板集成"; } } classGameComputerBuilder : ComputerBuilder { public GameComputerBuilder() { name = "GameComputer"; } publicoverridevoid SetupMainboard() { computer.Mainboard = "GIGABYTE技嘉GA-965P-DS3 3.3 主板(INTEL P965 东莞产)" ; } publicoverridevoid SetupCpu() { computer.Cpu = "Intel 英特尔酷睿E4400 (2.0GHz/LGA 775/2M/800MHz)盒装"; } publicoverridevoid SetupMemory() { computer.Memory = "G.SKILL 芝奇F2-6400CL5D-2GBNQ DDR2 800 1G*2台式机内存"; } publicoverridevoid SetupHarddisk() { computer.Harddisk = "Hitachi日立SATAII接口台式机硬盘(250G/7200转/8M)盒装"; } publicoverridevoid SetupVideocard() { computer.Videocard = "七彩虹逸彩GT-GD3 UP烈焰战神H10 显卡(GeForce 8600GT/256M/DDR3)支持HDMI!"; } } classComputer { privatestring videocard; publicstring Videocard { get { return videocard; } set { videocard = value; } } privatestring cpu; publicstring Cpu { get { return cpu; } set { cpu = value; } } privatestring mainboard; publicstring Mainboard { get { return mainboard; } set { mainboard = value; } } privatestring memory; publicstring Memory { get { return memory; } set { memory = value; } } privatestring harddisk; publicstring Harddisk { get { return harddisk; } set { harddisk = value; } } publicvoid ShowSystemInfo() { Console.WriteLine("==================SystemInfo=================="); Console.WriteLine("CPU:" + cpu); Console.WriteLine("MainBoard:" + mainboard); Console.WriteLine("Memory:" + memory); Console.WriteLine("VideoCard:" + videocard); Console.WriteLine("HardDisk:" + harddisk); } } } |
代码执行结果如下图:
代码说明
l ComputerFactory是建造者模式的指导者。指导者做的是稳定的建造工作,假设它就是一个技术人员,他只是在做按照固定的流程,把配件组装成计算机的重复劳动工作。他不知道他现在组装的是一台游戏电脑还是一台办公用电脑,他也不知道他往主板上安装的内存是1G还是2G的。呵呵,看来是不称职的技术人员。
l ComputerBuilder是抽象建造者角色。它主要是用来定义两种接口,一种接口用于规范产品的各个部分的组成。比如,这里就规定了组装一台电脑所需要的5个工序。第二种接口用于返回建造后的产品,在这里我们没有定义抽象方法,反正建造出来的总是电脑。
l OfficeComputerBuilder和GameComputerBuilder是具体的建造者。他的工作就是实现各建造步骤的接口,以及实现返回产品的接口,在这里后者省略了。
l Computer就是建造出来的复杂产品。在代码中,我们的各种建造步骤都是为创建产品中的各种配件服务的,Computer定义了一个相对具体的产品,在应用中可以把这个产品进行比较高度的抽象,使得不同的具体建造者甚至可以建造出完全不同的产品。
l 看看客户端的代码,用户先是选择了一个具体的Builder,用户应该很明确它需要游戏电脑还是办公电脑,但是它可以对电脑一无所知,由销售人员给出一个合理的配置单。然后用户让ComputerFactory去为它组装这个电脑。组装完成后ComputerFactory开机,给用户验收电脑的配置是否正确。
l 你或许觉得ComputerBuilder和是抽象工厂模式中的抽象工厂角色差不多,GameComputerBuilder又像是具体工厂。其实,建造者模式和抽象工厂模式的侧重点不同,前者强调一个组装的概念,一个复杂对象由多个零件组装而成并且组装是按照一定的标准射顺序进行的,而后者强调的是创建一系列产品。建造者模式适用于组装一台电脑,而抽象工厂模式适用于提供用户笔记本电脑、台式电脑和掌上电脑的产品系列。
何时采用
l 从代码角度来说, 如果你希望分离复杂类型构建规则和类型内部组成,或者希望把相同的构建过程用于构建不同类型的时候可以考虑使用建造者模式。
l 从应用角度来说, 如果你希望解耦产品的创建过程和产品的具体配件,或者你希望为所有产品的创建复用一套稳定并且复杂的逻辑的时候可以考虑使用建造者模式。
实现要点
l 对象的构建过程由指导者完成,具体的组成由具体建造者完成,表示与构建分离。
l 建造者和指导者是建造者模式的关键点,如果进行合并或省略就可能会转变到模版方法模式。
l 如果对象的建造步骤是简单的,并且产品拥有一致的接口可以转而使用工厂模式。
注意事项
l 返回产品的方法是否必须,是否一定要在抽象建造者中有接口根据实际情况而定。如果它们有统一的接口可以在抽象建造者中体现这个抽象方法,如果没有统一的接口(比如,生产毫无关联的产品)则可以在具体建造者中各自实现这个方法,如果创建的产品是一种产品,那么甚至可以省略返回产品的接口(本文的例子就是这样)。