建造者模式 生成器模式 建立型 设计模式(五)

建造者模式 Builder 也叫作生成器模式
在正式开始建造者模式以前,先回顾下抽象工厂模式
本人的全部系列文章都是本身学习的记录过程,均有比较严格的前后顺序,若是不清楚抽象工厂模式能够先往前翻翻

从抽象工厂演化

抽象工厂模式是工厂模式的进一步抽象扩展
不只仅能够建立某种等级结构的产品,能够建立一整个产品族的产品
以下图所示
好比ConcreteCreator1能够建立ConcreteProductA1和ConcreteProductB1
好比ConcreteCreator2能够建立ConcreteProductA2和ConcreteProductB2
 
一个产品族的两个产品,是相关联的或者有共同的约束,好比同一个厂家,运行在同一个平台下
image_5bebdce6_4d96

示例

考虑这样一种场景:
你是一个品牌电脑的组装厂家(组装代工厂),你拥有电脑的各类零部件(简单起见,仅仅以主板和显示器为例)
各类零部件都由多个厂家生产(简单起见,假设只有华硕和戴尔)
请你根据用户需求提供一台“品牌”电脑
 
涉及到多个产品等级结构,并且购买品牌电脑,同一品牌就是同一厂家,这是共同限制,有产品族的概念,因此可使用抽象工厂模式 
工厂生产相关联的产品族下的产品,而后进行组装
 
以下图所示
image_5bebdce6_301a
对于具体的工厂ConcreteCreator1能够建立华硕产品族的产品,主板和显示器
对于具体的工厂ConcreteCreator2能够建立戴尔产品族的产品,主板和显示器

代码

主板产品等级结构
image_5bebdce6_a12
package builder;
public interface MainBoard {
String desc();
}
package builder;
public class DellMainBoard implements MainBoard {
@Override
public String desc() {
return "DELL mainBoard";
}
}
package builder;
public class AsusMainBoard implements MainBoard {
@Override
public String desc() {
return "ASUS mainBoard";
}
}
显示器产品等级结构
image_5bebdce7_a15
package builder;
public interface DisplayDevice {
String Desc();
}
package builder;
public class DellDisplayDevice implements DisplayDevice {
@Override
public String Desc() {
return "DELL display device";
}
}
package builder;
public class AsusDisplayDevice implements DisplayDevice {
@Override
public String Desc() {
return "ASUS display device";
}
}
工厂体系结构
image_5bebdce7_68fc
package builder;
public interface Creator {
MainBoard createMainBoard();
DisplayDevice createDisplayDevice();
}
package builder;
public class ConcreateCreatorDell implements Creator {
@Override
public MainBoard createMainBoard() {
return new DellMainBoard();
}
@Override
public DisplayDevice createDisplayDevice() {
return new DellDisplayDevice();
}
}
package builder;
public class ConcreateCreatorAsus implements Creator {
@Override
public MainBoard createMainBoard() {
return new AsusMainBoard();
}

@Override
public DisplayDevice createDisplayDevice() {
return new AsusDisplayDevice();
}
}
电脑类Computer
用户须要的是一台电脑,电脑类为Computer
Computer包含主板和显示器部件 
重写了toString方便查看信息,toString中调用了主板和显示器的desc()方法
package builder;
public class Computer {
private MainBoard mainBoard;
private DisplayDevice displayDevice;
public MainBoard getMainBoard() {
return mainBoard;
}

public void setMainBoard(MainBoard mainBoard) {
this.mainBoard = mainBoard;
}

public DisplayDevice getDisplayDevice() {
return displayDevice;
}

public void setDisplayDevice(DisplayDevice displayDevice) {
this.displayDevice = displayDevice;
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Computer{");
sb.append("mainBoard=").append(mainBoard.desc());
sb.append(", displayDevice=").append(displayDevice.Desc());
sb.append('}');
return sb.toString();
}
}
测试代码客户端
image_5bebdce7_5564
 
以上,咱们就完成了需求
根据用户的需求,建立指定的产品族的产品,而且将这一系列的产品进行组合生成最终的用户须要的产品 

抽象工厂的问题

经过,抽象工厂模式进行产品族零部件的生产,而后在客户端进行加工
完成了咱们的需求,可是这其中有明显的问题
 
整个的组装细节与过程,所有暴露在客户端程序
客户端程序知道你全部的零部件类型,也知道你全部零部件实现的细节
顾客只是想购买一台电脑而已,人家为何要关心电脑到底有哪些部件,到底如何组装的?
简言之,你本身隐私暴露,人家还不稀罕看,嫌烦!
 
并且, 若是须要在多个场景中完成这个组装生成的过程,怎么办?将会出现大量的冗余代码
再者, 若是组装逻辑发生变更,须要维护多个地方,难度很是大
因此一个很天然的想法就是将整个的组装逻辑进行封装

为此,咱们新增长一个组装电脑类AssembleComputer
接受一个Creator 做为参数,借助于Creator进行零部件产品族的建立以及组装
package builder;
public class AssembleComputer {
Creator creator;
AssembleComputer(Creator creator){
this.creator = creator;
}

public Computer getComputer(){
Computer computer = new Computer();
MainBoard mainBoard = creator.createMainBoard();
DisplayDevice displayDevice = creator.createDisplayDevice();
computer.setMainBoard(mainBoard);
computer.setDisplayDevice(displayDevice);
return computer;
}
}
测试代码
image_5bebdce7_4302
 
通过封装后,这段代码看起来清爽多了,组装的细节被封装到了组装类AssembleComputer之中
客户端不在须要大量冗余的代码,并且后续扩展和维护也比较容易  

封装下的重构

上面的封装的组装逻辑中,咱们先把全部的零部件所有都生产出来,而后在一口气进行组装
虽然是把产品的组装细节对客户端程序隐藏了
可是,产品的表示生产和组装过程仍旧是耦合在一块儿的,都耦合在了getComputer()方法中
是否还能够进一步的将Computer的各个组成部分与组装逻辑分离呢?
咱们把工厂等级结构和组装电脑类重构下
package builder;

public interface CreatorRefactor {
void assembleMainBoard();
void assembleDisplayDevice();
Computer getComputer();
}
package builder;
public class ConcreateCreatorDellRefactor implements CreatorRefactor {
private Computer computer = new Computer();
@Override
public void assembleMainBoard() {
computer.setMainBoard(new DellMainBoard());
}
@Override
public void assembleDisplayDevice() {
computer.setDisplayDevice(new DellDisplayDevice());
}
@Override
public Computer getComputer() {
return computer;
}
}
package builder;
public class ConcreateCreatorAsusRefactor implements CreatorRefactor {
private Computer computer = new Computer();
@Override
public void assembleMainBoard() {
computer.setMainBoard(new AsusMainBoard());
}
@Override
public void assembleDisplayDevice() {
computer.setDisplayDevice(new AsusDisplayDevice());
}
@Override
public Computer getComputer() {
return computer;
}
}
能够看得出来重构以后的代码中
对于工厂角色来讲,不只仅是生产零部件,生产已经成为基础功能,还须要完成这一步骤的组装工做
而且提供最终返回完整产品的方法
具体的工厂中,建立了一个具体的产品引用,而且实现了抽象工厂的规定的协议-->组装每一个步骤以及最终返回具体产品
组装电脑类重构
package builder;
public class AssembleComputerRefactor {
CreatorRefactor creatorRefactor;
AssembleComputerRefactor(CreatorRefactor creatorRefactor){
this.creatorRefactor = creatorRefactor;
}

public Computer getComputer(){
creatorRefactor.assembleMainBoard();
creatorRefactor.assembleDisplayDevice();
return creatorRefactor.getComputer();
}
}
重构后的代码,组装电脑类AssembleComputerRefactor不在涉及到具体零部件的生产了
生产和每个零件步骤的组装已经移交到抽象工厂角色中了
组装电脑类仅仅涉及的就是组装流程!流程!流程! 彻底不关注具体的究竟是什么东西
 
测试代码
image_5bebdce7_3714
 
image_5bebdce7_43d6

小结

最终重构后的代码形式中:
生产产品的构造工厂CreatorRefactor,规定了负责构建一个完整产品的全部的步骤,而且返回最终产品
实际负责生产的具体工厂角色,实现规定的每一个步骤,而且返回最终的具体的产品
 
AssembleComputerRefactor仅仅包含产品的构建逻辑,也就是加工步骤
将生产产品的全部的步骤,合理的组织在一块儿
也便是说,它全部的流程的每个步骤的细节,都是工厂提供的,组装器这个流水线只是负责步骤的梳理安排
 
好比,穿衣服的全部步骤有:戴帽子,穿鞋子,穿袜子,穿裤子,穿内裤....
那么,这个组装器 就是将这些步骤合理组织安排
不能先穿鞋子在穿袜子的对吧,应该是 穿内裤,穿裤子,穿袜子,穿鞋子,戴帽子....
你看,步骤都是同样的,可是顺序多是有要求的,最终返回结果
其实这就是建造者模式
将复杂产品的构建过程和具体的产品进行分离
管你究竟是穿什么鞋子呢,反正你有穿鞋子这一步骤
管你到底穿哪条裤子,反正你得穿裤子,并且穿衣服的场景下得是先穿裤子才能穿鞋子

意图

将复杂对象的构建与他的表示进行分离,使得一样的构建过程能够建立不一样的表示
对象的构建与表示进行分离,就是相似组装过程与内部的零件的分离,一台电脑的内部表示是各类零件,构建就是组装的过程 

结构

咱们将前面的示例,转换为标准的建造者模式的称呼
image_5bebdce7_2e58
角色含义
抽象建造者角色Builder
给出一个抽象接口,以规范产品对象各个组成部分之间的构造
这个抽象的接口给出来构造一个产品的全部步骤以及最终产品的获取协议(就是其中定义的方法)(上面示例中的CreatorRefactor)
具体的建造者ConcreteBuilder
建立具体的产品的工厂、建造者
1.须要实现Builder中规定的产品建立的全部步骤
2.建造完成后,提供产品实例对象
(上面示例中的ConcreateCreatorDellRefactor 和 ConcreateCreatorAsusRefactor)
指挥者、导演Director
指挥产品的整个建造过程,不涉及具体产品的细节,只关注抽象建造者角色Builder定义的各个步骤的组织安排
具体的ConcreteBuilder 才会关注生产的细节(上面示例中的AssembleComputerRefactor)
产品 Product
最终建立起来的一个复杂的产品对象实例(上面示例中的Computer)
 

代码示例

image_5bebdce7_574c
 
package buildPattern;
public interface Builder {
void buildPart1();
void buildPart2();
Product buildProduct();
}
package buildPattern;
 
public class ConcreateBuilder implements Builder {
private Product product = new Product();
 
@Override
public void buildPart1() {
//...
}
 
@Override
public void buildPart2() {
//...
}
 
@Override
public Product buildProduct() {
return product;
}
}
package buildPattern;
public class Product {
}
package buildPattern;
 
public class Director {
 
private Builder builder;
 
Director(Builder builder) {
this.builder = builder;
}
 
public Product getProduct() {
builder.buildPart1();
builder.buildPart2();
return builder.buildProduct();
}
} 

注意事项

1. 上面示例中只有一个ConcreteBuilder,实际上固然能够有多个,他们都继承自抽象角色Builder   
 
2. 示例中Product为一个具体的类,固然能够变为抽象角色,这样全部的产品都是属于Product的
 
3. Builder角色中,咱们以组装电脑为例子,看起来好像必须是同一类产品,其实不是必然的
Builder与具体的业务逻辑不要紧,你能够把它简单的理解为步骤
好比它定义了五个步骤buildPart1(); buildPart2();  ........   buildPart5();
那么,好比汽车可能由五个生产步骤组成,好比房子能够有五个步骤建造 
Builder约定的只是流程,只是步骤,具体的细节由具体的实现工厂ConcreteBuilder决定了
究竟是五个盖房子的步骤,仍是五个造车子的步骤,具体的ConcreteBuilder说了算
 
4. 若是是很是抽象的几个步骤,彻底都不是一个类型的东西,那么这个抽象的产品怎么办?他们都没有任何的共性
你能够将返回结果产品的步骤,也就是最终的步骤,从抽象角色Builder中拿出来,每一个ConcreteBuilder本身返回本身的产品
或者
你能够提供一个标记接口,标记接口,标记接口,什么都不作,你就说 房子,车子,都是一种Product~~这样也能够解决

与抽象工厂对比

最开始咱们以抽象工厂模式引伸出建造者模式
在建造者模式中重要角色为Director和Builder,其实你会发现,其中的Builder与抽象工厂的Creator是有类似点的 
Director只不过是把ConcreteCreator中生产的产品族 进行组装
 
可是建造者模式中的Builder,重点不在于生产的零部件是什么,而是在于步骤的划分
固然每一个步骤可能也是须要“生产”的
能够认为Builder是抽象工厂模式中的Creator的一个变种
Director是抽象工厂模式生产产品后的进一步加工
他的重点在于步骤的组织安排
 
抽象工厂模式仅仅关注到我生产出来了这一个产品族的各个产品
建造者模式则进一步关注这些东西怎么构成、组装成为一个更加复杂的产品的步骤
 
若是以生产汽车为例
抽象工厂模式在于产生某一产品族的零部件,好比 轮胎 发动机 底盘
建造者模式在于安排建造的过程,安装底盘 安装轮胎 安装发动机
建造者模式的组装的每一个步骤中,可能须要先生产在组装,也可能只是多个加工步骤
与抽象工厂模式的对比是为了加深理解,若是反倒容易混淆,能够无视
 

使用场景

对于每种模式的使用场景,只须要理解透彻每种模式的意图便可
建造者模式的意图在于复杂对象的内部表示与建立过程进行分离前提就是面对复杂对象的建立 
好比
有不少品牌的笔记本电脑,电脑包括不少零部件 cpu 显卡 内存条 显示器等等 
有不少品牌的汽车,汽车包括不少零部件 底盘 发动机 轮胎 轮毂 等等
游戏中有不少我的物角色 他们都有 性别 发型 肤色 衣服 皮肤 等等
 
如何构造这些复杂的对象
并且还可以容易新增长新品牌的笔记本电脑和汽车,增长新的人物角色,也就是扩展性好
你就能够考虑建造者模式
建造者模式中的Director做为指挥者、导演,仅仅关心步骤的顺序安排
无论什么品牌的笔记本电脑,步骤都是同样的,安装cpu 安装显卡 安装内存条...等等
无论是什么品牌的汽车,生产步骤是同样的,安装底盘,安装发动机...等等
无论什么样子的人物角色,建立步骤是同样的,设置性别,设置肤色...等等
具体的建造者ConcreteBuilder才会关心每一个步骤到底作的是什么事情
 
将步骤与具体表示分离,当须要扩展时,Director部分彻底不须要变更,只须要增长新的ConcreteBuilder 便可
经过新的ConcreteBuilder , Director就能够建立出来新的产品、人物角色
 
建造者模式的关键就在于,复杂的对象,构建过程与内部表示的分离
因此当有复杂的内部结构时,或者步骤之间具备严格的顺序关系就能够考虑建造者模式
 
步骤不同是否可用?
上面反复强调,他们拥有相同的步骤
那么,若是一个产品拥有三个步骤,另一个产品拥有五个步骤
是否还可以使用建造者模式呢?
固然也是能够的
在抽象的Builder角色中,你仍然须要设置五个步骤
可是对于生产只须要三个步骤的产品的那个ConcreteBuilder
你能够将 buildPart4();buildPart5();
实现为空方法便可
好比 
void buildPart4(){
//什么都不作。。。。
}
 
因此,假如说,你定义了一个抽象角色Builder,他有N个步骤,那么他就能够构造1~N个步骤下,能够实现的全部产品!!!
细节由具体的ConcreteBuilder决定就行了,固然,通常你并不会那么作 
 

简化形式

设计模式都不是一成不变的,能够根据实际状况进行调整甚至变种
若是肯定系统中,只须要一个具体的建造者的话,那么就能够省略抽象的Builder角色
抽象的Builder就是为了规范多个具体Builder建造者的行为,若是只有一个具体的建造者,则失去了意义
此时,这个具体的建造者,也充当了抽象的Builder的角色
image_5bebdce7_49b4
 
若是已经省略了抽象的Builder
那么还能够继续省略Director角色
ConcreteBuilder,也充当了这个Director角色
ConcreteBuilder本身不只仅实现全部步骤的细节,而且还负责组装
说白了就是Director中的方法逻辑移植到ConcreteBuilder中, 客户端从ConcreteBuilder中获取产品  
 

建造者与构造方法

假设有一个MyObject类,他有不少属性,假定目前有v1~v7 ,总共7个
其中v1 和 v2 是必选,其他为可选属性
对于这种状况,咱们常用层叠的构造方法
层层嵌套调用
可是这种方式不够清晰,比较容易犯错,并且,不少时候即便参数写颠倒了,也并不会必定致使编译器报错
另一种方式就是借助于建造者模式的简化形式
以下面示例
package simplebuilder;
 
/**
* Created by noteless on 2018/10/17.
* Description:假定有一个MyObject类,有7个属性前面两个v1 和 v2 是必选,其他可选
*
* @author noteless
*/
public class MyObject {
 
private int v1;//必选
private int v2;//必选
private int v3;//可选
private int v4;//可选
private int v5;//可选
private int v6;//可选
private int v7;//可选
 
private static class Builder {
 
private int v1;
private int v2;
 
private int v3 = 0;
private int v4 = 0;
private int v5 = 0;
private int v6 = 0;
private int v7 = 0;
 
public Builder(int v1, int v2) {
this.v1 = v1;
this.v2 = v2;
}
 
public Builder setV3(int v3) {
this.v3 = v3;
return this;
}
 
public Builder setV4(int v4) {
this.v4 = v4;
return this;
}
 
public Builder setV5(int v5) {
this.v5 = v5;
return this;
}
 
public Builder setV6(int v6) {
this.v6 = v6;
return this;
}
 
public Builder setV7(int v7) {
this.v7 = v7;
return this;
}
 
public MyObject build() {
return new MyObject(this);
}
}
 
private MyObject(Builder builder) {
v1 = builder.v1;
v2 = builder.v2;
v3 = builder.v3;
v4 = builder.v4;
v5 = builder.v5;
v6 = builder.v6;
v7 = builder.v7;
}
 
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("MyObject{");
sb.append("v1=").append(v1);
sb.append(", v2=").append(v2);
sb.append(", v3=").append(v3);
sb.append(", v4=").append(v4);
sb.append(", v5=").append(v5);
sb.append(", v6=").append(v6);
sb.append(", v7=").append(v7);
sb.append('}');
return sb.toString();
}
 
public static void main(String[] args) {
 
MyObject my = new MyObject.Builder(1, 2).
setV3(3).setV4(4).setV5(5).setV6(6).setV7(7).build();
System.out.println(my.toString());
}
}
省略了抽象的Builder,也省略了Director角色
示例中的Builder 就是模式中的ConcreteBuilder角色
他负责每个步骤的实现细节,而且提供方法build()  获取最终的产品角色对象
借助于简化的工厂模式进行构造方法的替换解决方案的巧妙之处在于:

public MyObject build() {web

return new MyObject(this);设计模式

}app

它借助于建造者模式将实现与过程进行分离
可是在build() 方法中又并无严格的规定步骤的过程
只是在构造Builder时必须传递两个必须参数,其他的参数你能够设置,也能够不设置 
达到了多层嵌套构造方法的效果
并且,还很是清晰,你不会那么轻易地就在设置参数时犯错,由于你须要调用指定的方法

总结

本文经过抽象工厂模式演化到建造者模式,看到了建造者模式与抽象工厂模式的细节差别
建造者自己并不复杂,只须要理解本意便可“复杂对象的构建过程与表示进行分离”
建造者模式是将“步骤”这一事物进行抽象化,抽象化为Builder,将事物的表示延迟到子类ConcreteBuilder中,并经过Director进行组装 
核心就是将“步骤”这一事物抽象
对于涉及到复杂对象的表示的场景,均可以考虑建造者模式
从抽象工厂的演进咱们能够看得出来,建造者模式,能够借助于抽象工厂模式进行实现 
相关文章
相关标签/搜索