设计模式-建造者模式
2019-08-04 本文已影响8人
NealLemon
简介
定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
相同的构建过程,可以创建不同的产品。比较试用于流程固定,但是顺序不固定的场景。
特点
用户只需指定需要建造的类型就可以得到他们,建造过程以及细节不需要知道。
适用场景
- 一个对象有复杂的内部结构
- 属性
- 步骤
- 把复杂对象的创建和使用分离
优点
- 封装性好,创建和使用分离。
- 扩展性好,建造类之间独立,一定程度上解耦。
缺点
- 产生多余的Builder对象
- 修改内部结构,成本较大
代码示例
这里举一个日常的例子,比如我们日常去饭店吃饭。
基本都会涉及到以下几个步骤:
- 点餐
- 选择堂食还是外带
- 付款
- 就餐
- 打包
当然以上几步骤不是固定的,因此我们使用建造者模式来实现,比较符合场景。
首先我们需要一个吃货类
在这个吃货类中,我们拥有以上的吃饭流程,这里有一个枚举类代表吃饭的形式。
public class Eater {
private String order; //点餐
private EatMode mode; //堂食还是外带
private String pay; //付款
private String eat; //就餐
private String pack; //打包
}
/**
* 就餐模式
*/
public enum EatMode {
HERE("堂食"),
OUTSIDE("外带");
private String label;
EatMode(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
}
接着我们需要一个建造类
我们将这个建造类以静态类的方式放在吃货类中。
/**
* 食客
*/
public class Eater {
private String order; //点餐
private EatMode mode; //堂食还是外带
private String pay; //付款
private String eat; //就餐
private String pack; //打包
public Eater(EaterBuilder eaterBuilder) {
this.order = eaterBuilder.order;
this.mode = eaterBuilder.mode;
this.pack = eaterBuilder.pack;
this.eat = eaterBuilder.eat;
this.pay = eaterBuilder.pay;
}
@Override
public String toString() {
return "Eater{" +
"order='" + order + '\'' +
", mode=" + mode.getLabel() +
", pay='" + pay + '\'' +
", eat='" + eat + '\'' +
", pack='" + pack + '\'' +
'}';
}
public static class EaterBuilder {
private String order;//点餐
private EatMode mode; //堂食还是外带
private String pay; //付款
private String eat; //就餐
private String pack; //打包
public EaterBuilder builderOrder(String order) {
this.order = order;
return this;
}
public EaterBuilder builerMode(EatMode eatMode) {
this.mode = eatMode;
return this;
}
public EaterBuilder builerPay(String pay) {
this.pay = pay;
return this;
}
public EaterBuilder builerEat(String eat) {
this.eat = eat;
return this;
}
public EaterBuilder builderPack(String pack) {
this.pack = pack;
return this;
}
public Eater build() {
return new Eater(this);
}
}
}
在这个建造类中,我们建造了各种步骤,最后还包括了建造方法。 简单的建造者模式就完成了。接下来我们来看一下效果。
就餐体验开始
我们会选择在肯德基和正常饭店吃饭两种情景来演示,首先我们知道,
一般我们去肯德基吃饭的步骤是
- 点餐
- 选择堂食还是带走
- 结账
- 吃吃吃
而我们去饭店吃饭的步骤可能是
- 点餐
- 选择堂食
- 吃吃吃
- 结账
- 打包
这两种正符合我们在一开始定义中的描述。我们简单的实现即可。
public class EaterBootStrap {
public static void main(String[] args) {
//去肯德基吃饭
Eater kfc = new Eater.EaterBuilder().
builderOrder("肯德基点餐").builerMode(EatMode.HERE).
builerPay("结账:100 RMB").builerEat("吃吃吃").build();
System.out.println("肯德基就餐:" + kfc.toString());
//去饭店吃饭
Eater restaurant = new Eater.EaterBuilder().
builderOrder("饭店点餐").builerMode(EatMode.HERE).
builerEat("吃吃吃").builerPay("结账: 89 RMB").builderPack("打包剩菜").build();
System.out.println("饭店就餐:" + restaurant.toString());
}
}
框架源码举例
JDK
JDK中,我们在拼接字符串的时候,经常使用的就是StringBuilder
或者 StringBuffer
。那么我们来看看内部的设计模式。
我们来看一下StringBuffer
中的 append()
方法
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
我们清晰的看到,这里也使用了和我们上面demo很类似的方式,就是建造者模式的主要体现。
Spring
在Spring中,我们也有建造者模式的实现。
BeanDefinitionBuilder
类中的实现:
public static BeanDefinitionBuilder genericBeanDefinition(Class<?> beanClass) {
BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition());
builder.beanDefinition.setBeanClass(beanClass);
return builder;
}
总结
设计模式讲究的是一种平衡以及针对业务模型的匹配。不要滥用,徒增麻烦。