一、概念
将一个复杂对像的构建与它的表示分离,使得同样的构建过程创建不同的表示,又叫建造模式。
生成器模式的重心在于分离构建算法和具体的构造实现,从而使得构建算法可以重用。采用不同的构建实现,产生不同的产品。所以生成器模式都会存在以下两个部分:
a.整体构建算法
b.部件的构造和产品的装配
二、模式动机
生成器模式主要的功能就是构建复杂的产品,且是细化的、分步骤的构建产品,生成器模式重在一步一步解决构造复杂对像的问题。
三、模式的结构
示意代码:
/** * * @ClassName: Product * @Description: TODO(被构建的产品对像接品) * @author beteman6988* @date 2017年10月1日 上午11:26:33 * */public interface Product {}
/** * 成生器接口,定义构建一个产品对像所需要的各个部件的操作* @ClassName: Builder * @author beteman6988* @date 2017年10月1日 上午11:28:12 * */public interface Builder { /** * 构建某个部件 * @Title: buildPart * @param * @return void * @throws */ public void buildPart(); /** * 获取最终生成的产品对像 * @Title: getResult * @param @return * @return Product * @throws */ public Product getResult();}
public class ConcreteBuilder implements Builder { //最终构建的产品 private Product resultProduct; /** * 获取最终生成的产品 * @Title: getResult * @param @return * @return Product * @throws */ @Override public Product getResult() { return this.resultProduct; } @Override public void buildPart() { // TODO Auto-generated method stub }}
public class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } /** * 指导生成器构建最终的产品对像 * @Title: construct * @param * @return void * @throws */ public void construct() { builder.buildPart(); builder.getResult(); }}
a.Builder:生成器接口,定义创建一个Product对像所需要的各个部件的操作。
b.ConcreteBuilder:具体生成器的实现,实现各个部件的创建,并负责组装Product对像的各个部件,同时还提供一个让用户获取组装完毕后的产品对像的方法。
c.Director:指导者,抽像出来的整体构建算法,主要用来使用Builder,以一个统一的过程来构建所需要的Product对像。
d.Product:产品,表示被生成器构建的复杂对像,包含多个部件。
四、模式样例
以一次台式机组装经历为例,当我们去电脑城组装电脑时,首先导购小妹妹会拿出一张打印好的空白配置报价单,我们在导购小妹的引导下,填写(当然小导购小妹填写)完了报价单后,然后组装工程师小哥就会跟据这个报价单,去仓库领取所有配件,然后就会将所有的配件组装在一起,一个完整的computer就组装好了,最后我们交钱拿电脑走人。
运用生成器模式对上述过程进行分析:
a.Product:导购小妹给我们的那个报价单可以说是一台最终computer product的一个抽像,当我们填写完一个一个完整的报价单后,可以说一个具体的compueter已经有了,
不过目前还是一堆零部件,要想得到一台完整的computer,还需要一个组装的过程。
b.Builder:我们的组装工程师小哥,跟据不同的具体报价单,都要去仓库忙一翻,要找到所有的部件,可以说只要有报价单,小哥都要将具体产口的每个部件都要找出来,少一个可以说都不行,这个可以说是一个完整电脑每个部件的抽像的建造(只是去仓库的相关位置拿到相关部件)。
c.ConcreteBuilder:组装工程师小哥跟据具体的报价单,去仓库找到相应的配件,并对每个部件进行处理,该拆封的拆封,该插线的插线,然后依据一定的步骤组装将所有配件组装起来,组装成一台完整的电脑。
d.Director:电脑的组装可以说有固定的套路,可以说这个套路适应于所有的电脑组装,比如必须先有机箱,然后将主板装到机箱上,然后将cpu装到主板上,步骤不能乱。按这个套路,工程师小哥跟据不同的报价单组装出不同的computer.
代码如下:
public interface CBuilder { /** * 构建机箱 * @Title: buildBox * @param * @return void * @throws */ public void buildBox(); /** * 安装主板 * @Title: buildMBoard * @param * @return void * @throws */ public void buildMBoard(); /** * 安装CPU * @Title: buildCpu * @param * @return void * @throws */ public void buildCpu(); /** * 安装内存 * @Title: buildMemory * @param * @return void * @throws */ public void buildMemory(); /** * 安装硬盘 * @Title: buildHDisk * @param * @return void * @throws */ public void buildHDisk(); /** * 返回安装完毕的计算机 * @Title: getComputer * @param @return * @return ComputerA * @throws */ public ComputerA getComputer();}
public class ComputerABuilder implements CBuilder {
private ComputerA aComputer=new ComputerA(); @Override public void buildBox() { this.aComputer.setBox("MAtx"); } @Override public void buildMBoard() { this.aComputer.setmBoard("AsusMb"); } @Override public void buildCpu() { this.aComputer.setCpu("Intel CoreI7"); } @Override public void buildMemory() { this.aComputer.setMemory("King stone DDR4"); } @Override public void buildHDisk() { this.aComputer.sethDisk("West Hard Disk 1T"); } @Override public ComputerA getComputer() { // TODO Auto-generated method stub System.out.println("Box:"+this.aComputer.getBox()); System.out.println("MBoard:"+this.aComputer.getmBoard()); System.out.println("Cpu:"+this.aComputer.getCpu()); System.out.println("Memory:"+this.aComputer.getMemory()); System.out.println("HDisk:"+this.aComputer.gethDisk()); return aComputer; }}
public class ComputerA { private String box; private String mBoard; private String cpu; private String memory; private String hDisk; public ComputerA() { super(); } public String getBox() { return box; } public void setBox(String box) { this.box = box; } public String getmBoard() { return mBoard; } public void setmBoard(String mBoard) { this.mBoard = mBoard; } public String getCpu() { return cpu; } public void setCpu(String cpu) { this.cpu = cpu; } public String getMemory() { return memory; } public void setMemory(String memory) { this.memory = memory; } public String gethDisk() { return hDisk; } public void sethDisk(String hDisk) { this.hDisk = hDisk; }}
public class CDirector { private CBuilder theCBuilder; /** * @roseuid 59DB89BE02A1 */ public CDirector(CBuilder builder) { this.theCBuilder=builder; } /** * 独立出来的构建过程 */ public void construct() { this.theCBuilder.buildBox(); this.theCBuilder.buildMBoard(); this.theCBuilder.buildCpu(); this.theCBuilder.buildMemory(); this.theCBuilder.buildHDisk(); this.theCBuilder.getComputer(); }}
public class Client { public static void main(String[] args) { CBuilder builder=new ComputerABuilder(); CDirector director=new CDirector(builder); director.construct(); }}
运行结果如下:
Box:MAtx
MBoard:AsusMb Cpu:Intel CoreI7 Memory:King stone DDR4 HDisk:West Hard Disk 1T
五、模式的约束
对于以下情况应当使用生成器模式:
1.需要生成的产品对像有复杂的内部结构。每个内部成分可以是一个对像,也可以是产品的一个组成部分。
2.需要生产的产品的各个部件之间相互依赖,也就是一个部件的建造必须依赖另一个部件建造完成才能建造,生成器模式可以强制实行一种分步骤的建造过程。
3.在对像的创建过程中会使用到系统中的其它一些对像,这些对像在产品对像的创建过程中不易得到。
六、模式的变体与扩展
1.省略抽像生成器Builder角色,如果系统确定只会有一个具体生成器角色,那么就可以省略掉抽像生成器角色,类图如下:
2.省略指导者角色Director :如果抽像生成器Builder角色被省略,如上面的“1.”的情况,那么Director存在的意义就不大了,该角色也可以进行省略,如下图:
七、与其它模式的关系
1.生成器模式与工厂方法模式,这两个模式可以组合使用。生成器模式的Builder实现中,通常需要选择具体的部件实现。一个可行的方案就是实现为工厂方法,通常工厂方法来获取具体的部件对像,然后再进行部件的装配。
如下图:
2.生成器模式与抽像工厂模式,这两个模式是可以组合使用的,在生成器模式的Builder实现中,需要创建各个部件对像,而这些部件对像是有关联的,通常是构成的是一个复杂对像的部件对像。也就是说,Builder实现中,需要获取构成一个复杂对像的产品族,那自然就可以使用抽像工厂模式来实现,这样一来抽像工厂负责了部件对像的创建,Builder实现里面则主要负责产品对像的整体构建了。
如下图:
其实创建模式和抽像工厂模式两者还是有区别的,抽像工厂模式的主要目的是创建产品族,这个产品族里面的单个产品就相当于是构成一个复杂对像的部件对像,抽像工厂对像创建完成后就立即返回整个产品族;而生成器的模式的主要目的是按照构造算法,一步一步来构建一个复杂的产品对像,通常要等到整个构建过程结速后,才会得到最终的产品对像。
八、模式优缺点
生成器模式可以用同一个构建算法构建出表现上完全不同的产品,实现产品的构建和产品的表现上的分离。生成器模式正是把产品的构建过程独立出来,使它和具体产品的表现松散耦合,从而使得构建算法可以重用,而具体产品表现可以扩展和切换,从产品表现维度很好的支持开闭原则。