30.1 工厂方法模式VS建造者模式
工厂方法模式注重的是整体对象的创建方法,而建造者模式注重的是部件构建的过程,旨在通过一步一步地精确构造创建出一个复杂的对象。我们举个简单例子来说明两者的差异,如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷、能够飞翔、内裤外穿的超人;而如果使用建造者模式,则需要组装手、头、脚、躯干等部分,然后再把内裤外穿,于是一个超人就诞生了。纯粹使用文字来描述比较枯燥,我们还是通过程序来更加清晰地认识两者的差别。
30.1.1 按工厂方法建造超人
首先,按照工厂方法模式创建出一个超人,类图如图30-1所示。
![](https://box.kancloud.cn/2016-08-14_57b0036cba8a6.jpg)
图30-1 按工厂方法建造超人
类图中我们按照年龄段把超人分为两种类型:成年超人(如克拉克、超能先生)和未成年超人(如Dash、Jack)。这是一个非常正宗的工厂方法模式,定义一个产品的接口,然后再定义两个实现,通过超人制造工厂制造超人。想想看我们对超人最大印象是什么?当然是他的超能力,我们以specialTalent(特殊天赋)方法来代表,先看抽象产品类,如代码清单30-1所示。
代码清单30-1 超人接口
public interface ISuperMan {
//每个超人都有特殊技能
public void specialTalent();
}
产品的接口定义好了,我们再来看具体的产品。先看成年超人,很简单,如代码清单30-2所示。
代码清单30-2 成年超人
public class AdultSuperMan implements ISuperMan {
//超能先生
public void specialTalent() {
System.out.println("超人力大无穷");
}
}
未成年超人的代码如代码清单30-3所示。
代码清单30-3 未成年超人
public class ChildSuperMan implements ISuperMan {
//超能先生的三个孩子
public void specialTalent() {
System.out.println("小超人的能力是刀枪不入、快速运动");
}
}
产品都具备,那我们编写一个工厂类,其意图就是生产超人,具体是成年超人还是未成年超人,则由客户端决定,如代码清单30-4所示。
代码清单30-4 超人制造工厂
public class SuperManFactory {
//定义一个生产超人的工厂
public static ISuperMan createSuperMan(String type){
//根据输入参数产生不同的超人
if(type.equalsIgnoreCase("adult")){
//生产成人超人
return new AdultSuperMan();
}else if(type.equalsIgnoreCase("child")){
//生产未成年超人
return new ChildSuperMan();
}else{
return null;
}
}
}
产品有了,工厂类也有了,剩下的工作就是开始生产超人。这也非常简单,如代码清单30-5所示。
代码清单30-5 场景类
public class Client {
//模拟生产超人
public static void main(String[] args) {
//生产一个成年超人
ISuperMan adultSuperMan = SuperManFactory.createSuperMan("adult");
//展示一下超人的技能
adultSuperMan.specialTalent();
}
}
建立了一个超人生产工厂,年复一年地生产超人,对于具体生产出的产品,不管是成年超人还是未成年超人,都是一个模样:深蓝色紧身衣、胸前S标记、内裤外穿,没有特殊的地方。但是我们的目的达到了——生产出超人,拯救全人类,这就是我们的意图。具体怎么生产、怎么组装,这不是工厂方法模式要考虑的,也就是说,工厂模式关注的是一个产品整体,生产出的产品应该具有相似的功能和架构。
注意 通过工厂方法模式生产出对象,然后由客户端进行对象的其他操作,但是并不代表所有生产出的对象都必须具有相同的状态和行为,它是由产品所决定。
30.1.2 按建造者模式建造超人
我们再来看看建造者模式是如何生产超人的,如图30-2所示。
![](https://box.kancloud.cn/2016-08-14_57b0036cce92e.jpg)
图30-2 按建造者模式生产超人
又是一个典型的建造者模式!哎,不对呀!通用模式上抽象建造者与产品类没有关系呀!是的,我们当然可以加强了,我们在抽象建造者上使用了模板方法模式,每一个建造者都必须返回一个产品,但是产品是如何制造的,则由各个建造者自己负责。我们来看看程序,先看产品类,如代码清单30-6所示。
代码清单30-6 超人产品
public class SuperMan {
//超人的躯体
private String body;
//超人的特殊技能
private String specialTalent;
//超人的标志
private String specialSymbol;
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getSpecialTalent() {
return specialTalent;
}
public void setSpecialTalent(String specialTalent) {
this.specialTalent = specialTalent;
}
public String getSpecialSymbol() {
return specialSymbol;
}
public void setSpecialSymbol(String specialSymbol) {
this.specialSymbol = specialSymbol;
}
}
超人这个产品是由三部分组成:躯体、特殊技能、身份标记,这就类似于电子产品,首先生产出一个固件,然后再安装一个灵魂(软件驱动),最后再打上产品标签。完事了!一个崭新的产品就诞生了!我们的超人也是这样生产的,先生产一个普通的躯体,然后注入特殊技能,最后打上S标签,一个超人生产完毕。我们再来看一下建造者的抽象定义,如代码清单30-7所示。
代码清单30-7 抽象建造者
public abstract class Builder {
//定义一个超人的应用
protected final SuperMan superMan = new SuperMan();
//构建出超人的躯体
public void setBody(String body){
this.superMan.setBody(body);
}
//构建出超人的特殊技能
public void setSpecialTalent(String st){
this.superMan.setSpecialTalent(st);
}
//构建出超人的特殊标记
public void setSpecialSymbol(String ss){
this.superMan.setSpecialSymbol(ss);
}
//构建出一个完整的超人
public abstract SuperMan getSuperMan();
}
一个典型的模板方法模式,超人的各个部件(躯体、灵魂、标志)都准备好了,具体怎么组装则是由实现类来决定。我们先来看成年超人,如代码清单30-8所示。
代码清单30-8 成年超人建造者
public class AdultSuperManBuilder extends Builder {
@Override
public SuperMan getSuperMan() {
super.setBody("强壮的躯体");
super.setSpecialTalent("会飞行");
super.setSpecialSymbol("胸前带S标记");
return super.superMan;
}
}
怎么回事?在第11章中讲解建造者模式的时候在产品中使用了模板方法模式,在这里怎么把模板方法模式迁移到建造者了?怎么会这样?你是不是在发出这样的疑问?别疑问了!设计模式只是提供了一个解决问题的意图:复杂对象的构建与它的表示分离,而没有具体定出一个设计模式必须是这样的实现,必须是这样的代码,灵活运用模式才是其根本,别学死板了。
我们继续看未成年超人的建造者,如代码清单30-9所示。
代码清单30-9 未成年超人建造者
public class ChildSuperManBuilder extends Builder {
@Override
public SuperMan getSuperMan() {
super.setBody("强壮的躯体");
super.setSpecialTalent("刀枪不入");
super.setSpecialSymbol("胸前带小S标记");
return super.superMan;
}
}
大家注意看我们这两个具体的建造者,它们都关注了产品的各个部分,在某些应用场景下甚至会关心产品的构建顺序,即使是相同的部件,装配顺序不同,产生的结果也不同,这也正是建造者模式的意图:通过不同的部件、不同装配产生不同的复杂对象。我们再来看导演类,如代码清单30-10所示。
代码清单30-10 导演类
public class Director {
//两个建造者的应用
private static Builder adultBuilder = new AdultSuperManBuilder();
//未成年超人的建造者
private static Builder childBuilder = new ChildSuperManBuilder();
//建造一个成年、会飞行的超人
public static SuperMan getAdultSuperMan(){
return adultBuilder.getSuperMan();
}
//建造一个未成年、刀枪不入的超人
public static SuperMan getChildSuperMan(){
return childBuilder.getSuperMan();
}
}
这很简单,不多说了!看看场景类是如何调用的,如代码清单30-11所示。
代码清单30-11 场景类
public class Client {
public static void main(String[] args) {
//建造一个成年超人
SuperMan adultSuperMan = Director.getAdultSuperMan();
//展示一下超人的信息
adultSuperMan.getSpecialTalent();
}
}
这个场景类的写法与工厂方法模式是相同的,但是你可以看到,在建立超人的过程中,建造者必须关注超人的各个部件,而工厂方法模式则只关注超人的整体,这就是两者的区别。
30.1.3 最佳实践
工厂方法模式和建造者模式都属于对象创建类模式,都用来创建类的对象。但它们之间的区别还是比较明显的。
● 意图不同
在工厂方法模式里,我们关注的是一个产品整体,如超人整体,无须关心产品的各部分是如何创建出来的;但在建造者模式中,一个具体产品的产生是依赖各个部件的产生以及装配顺序,它关注的是“由零件一步一步地组装出产品对象”。简单地说,工厂模式是一个对象创建的粗线条应用,建造者模式则是通过细线条勾勒出一个复杂对象,关注的是产品组成部分的创建过程。
● 产品的复杂度不同
工厂方法模式创建的产品一般都是单一性质产品,如成年超人,都是一个模样,而建造者模式创建的则是一个复合产品,它由各个部件复合而成,部件不同产品对象当然不同。这不是说工厂方法模式创建的对象简单,而是指它们的粒度大小不同。一般来说,工厂方法模式的对象粒度比较粗,建造者模式的产品对象粒度比较细。
两者的区别有了,那在具体的应用中,我们该如何选择呢?是用工厂方法模式来创建对象,还是用建造者模式来创建对象,这完全取决于我们在做系统设计时的意图,如果需要详细关注一个产品部件的生产、安装步骤,则选择建造者,否则选择工厂方法模式。
- 前言
- 第一部分 大旗不挥,谁敢冲锋——6大设计原则全新解读
- 第1章 单一职责原则
- 1.2 绝杀技,打破你的传统思维
- 1.3 我单纯,所以我快乐
- 1.4 最佳实践
- 第2章 里氏替换原则
- 2.2 纠纷不断,规则压制
- 2.3 最佳实践
- 第3章 依赖倒置原则
- 3.2 言而无信,你太需要契约
- 3.3 依赖的三种写法
- 3.4 最佳实践
- 第4章 接口隔离原则
- 4.2 美女何其多,观点各不同
- 4.3 保证接口的纯洁性
- 4.4 最佳实践
- 第5章 迪米特法则
- 5.2 我的知识你知道得越少越好
- 5.3 最佳实践
- 第6章 开闭原则
- 6.2 开闭原则的庐山真面目
- 6.3 为什么要采用开闭原则
- 6.4 如何使用开闭原则
- 6.5 最佳实践
- 第二部分 真刀实枪 ——23种设计模式完美演绎
- 第7章 单例模式
- 7.2 单例模式的定义
- 7.3 单例模式的应用
- 7.4 单例模式的扩展
- 7.5 最佳实践
- 第8章 工厂方法模式
- 8.2 工厂方法模式的定义
- 8.3 工厂方法模式的应用
- 8.4 工厂方法模式的扩展
- 8.5 最佳实践
- 第9章 抽象工厂模式
- 9.2 抽象工厂模式的定义
- 9.3 抽象工厂模式的应用
- 9.4 最佳实践
- 第10章 模板方法模式
- 10.2 模板方法模式的定义
- 10.3 模板方法模式的应用
- 10.4 模板方法模式的扩展
- 10.5 最佳实践
- 第11章 建造者模式
- 11.2 建造者模式的定义
- 11.3 建造者模式的应用
- 11.4 建造者模式的扩展
- 11.5 最佳实践
- 第12章 代理模式
- 12.2 代理模式的定义
- 12.3 代理模式的应用
- 12.4 代理模式的扩展
- 12.5 最佳实践
- 第13章 原型模式
- 13.2 原型模式的定义
- 13.3 原型模式的应用
- 13.4 原型模式的注意事项
- 13.5 最佳实践
- 第14章 中介者模式
- 14.2 中介者模式的定义
- 14.3 中介者模式的应用
- 14.4 中介者模式的实际应用
- 14.5 最佳实践
- 第15章 命令模式
- 15.2 命令模式的定义
- 15.3 命令模式的应用
- 15.4 命令模式的扩展
- 15.5 最佳实践
- 第16章 责任链模式
- 16.2 责任链模式的定义
- 16.3 责任链模式的应用
- 16.4 最佳实践
- 第17章 装饰模式
- 17.2 装饰模式的定义
- 17.3 装饰模式应用
- 17.4 最佳实践
- 第18章 策略模式
- 18.2 策略模式的定义
- 18.3 策略模式的应用
- 18.4 策略模式的扩展
- 18.5 最佳实践
- 第19章 适配器模式
- 19.2 适配器模式的定义
- 19.3 适配器模式的应用
- 19.4 适配器模式的扩展
- 19.5 最佳实践
- 第20章 迭代器模式
- 20.2 迭代器模式的定义
- 20.3 迭代器模式的应用
- 20.4 最佳实践
- 第21章 组合模式
- 21.2 组合模式的定义
- 21.3 组合模式的应用
- 21.4 组合模式的扩展
- 21.5 最佳实践
- 第22章 观察者模式
- 22.2 观察者模式的定义
- 22.3 观察者模式的应用
- 22.4 观察者模式的扩展
- 22.5 最佳实践
- 第23章 门面模式
- 23.2 门面模式的定义
- 23.3 门面模式的应用
- 23.4 门面模式的注意事项
- 23.5 最佳实践
- 第24章 备忘录模式
- 24.2 备忘录模式的定义
- 24.3 备忘录模式的应用
- 24.4 备忘录模式的扩展
- 24.5 最佳实践
- 第25章 访问者模式
- 25.2 访问者模式的定义
- 25.3 访问者模式的应用
- 25.4 访问者模式的扩展
- 25.5 最佳实践
- 第26章 状态模式
- 26.2 状态模式的定义
- 26.3 状态模式的应用
- 第27章 解释器模式
- 27.2 解释器模式的定义
- 27.3 解释器模式的应用
- 27.4 最佳实践
- 第28章 享元模式
- 28.2 享元模式的定义
- 28.3 享元模式的应用
- 28.4 享元模式的扩展
- 28.5 最佳实践
- 第29章 桥梁模式
- 29.2 桥梁模式的定义
- 29.3 桥梁模式的应用
- 29.4 最佳实践
- 第三部分 谁的地盘谁做主 ——设计模式PK
- 第30章 创建类模式大PK
- 30.1 工厂方法模式VS建造者模式
- 30.2 抽象工厂模式VS建造者模式
- 第31章 结构类模式大PK
- 31.1 代理模式VS装饰模式
- 31.2 装饰模式VS适配器模式
- 第32章 行为类模式大PK
- 32.1 命令模式VS策略模式
- 32.2 策略模式VS状态模式
- 32.3 观察者模式VS责任链模式
- 第33章 跨战区PK
- 33.1 策略模式VS桥梁模式
- 33.2 门面模式VS中介者模式
- 33.3 包装模式群PK
- 第四部分 完美世界 ——设计模式混编
- 第34章 命令模式+责任链模式
- 34.2 混编小结
- 第35章 工厂方法模式+策略模式
- 35.2 混编小结
- 第36章 观察者模式+中介者模式
- 36.2 混编小结
- 第五部分 扩展篇
- 第37章 MVC框架
- 37.2 最佳实践
- 第38章 新模式
- 38.1 规格模式
- 38.2 对象池模式
- 38.3 雇工模式
- 38.4 黑板模式
- 38.5 空对象模式
- 附录 23种设计模式彩图