【设计模式】一文速通建造者模式小九开了一家汉堡店,客人可以根据自己的口味定制汉堡,汉堡有多种配料可选: 面包:全麦面包,
1. 引入
小九开了一家汉堡店,客人可以根据自己的口味定制汉堡,汉堡有多种配料可选:
- 面包:全麦面包,白面包...
- 肉饼:鸡肉,牛肉...
- 蔬菜:生菜,酸黄瓜,番茄...
- 酱料:番茄酱,蜂蜜芥末酱...
不同客人的口味不同,如何根据客人需要去创建客人想要的汉堡呢?以下是两种实现方案。
1.1 方案一:构造方法实现
1.1.1 实现代码
- 定义实体类
Burger
,实体类中定义构造方法
@Data
public class Burger {
// 面包
private String bread;
// 肉饼
private String patty;
// 蔬菜
private List<String> vegetables;
// 酱料
private List<String> sauces;
// 汉堡的构造方法
public Burger(String bread, String patty, List<String> vegetables, List<String> sauces) {
this.bread = bread;
this.patty = patty;
this.vegetables = vegetables;
this.sauces = sauces;
}
@Override
public String toString() {
return "Burger{" +
"bread='" + bread + '\'' +
", patty='" + patty + '\'' +
", vegetables=" + vegetables +
", sauces=" + sauces +
'}';
}
}
- 根据客人需求创建
Burger
对象
public static void main(String[] args) {
// 客人选择的蔬菜
List<String> vegetables = Arrays.asList("Lettuce", "Tomato");
// 客人选择的酱料
List<String> sauces = Arrays.asList("Ketchup");
// 通过构造方法创建burger
Burger burger = new Burger("Sesame", "Beef", vegetables, sauces);
System.out.println(burger);
}
1.1.2 上述方案存在问题
- 构造函数参数多:构造函数参数多且顺序复杂,容易出错;如果将来需要添加更多属性,构造函数的参数列表会很多且难以管理;
- 可读性差:创建对象时很难知道每个参数的意义,尤其是当参数多的时候;
- 灵活性差:难以扩展和修改对象的构造方式,一旦增加新属性,需要修改构造函数和相关代码。
1.2 方案二:采用建造者模式实现
使用建造者模式相当于定义了一个汉堡定做器,通过汉堡定做器可以一步步指定每种配料,最后得到一个完全符合客户要求的汉堡。
- 定义
Builder
接口,定义构建汉堡的步骤。
public interface Builder {
Builder setBread(String bread);
Builder setPatty(String patty);
Builder addVegetable(String vegetable);
Builder addSauce(String sauce);
Burger build();
}
- 具体的建造者,实现
Builder
接口,提供了如何构建一个具体的汉堡。
public class BurgerBuilder implements Builder {
private String bread;
private String patty;
private List<String> vegetables = new ArrayList<>();
private List<String> sauces = new ArrayList<>();
@Override
public Builder setBread(String bread) {
this.bread = bread;
return this;
}
@Override
public Builder setPatty(String patty) {
this.patty = patty;
return this;
}
@Override
public Builder addVegetable(String vegetable) {
this.vegetables.add(vegetable);
return this;
}
@Override
public Builder addSauce(String sauce) {
this.sauces.add(sauce);
return this;
}
@Override
public Burger build() {
return new Burger(new Burger.Builder()
.setBread(this.bread)
.setPatty(this.patty)
.addVegetable(this.vegetables)
.addSauce(this.sauces));
}
}
- 定义
Burger
实体类,表示最终创建的汉堡。
public class Burger {
private String bread;
private String patty;
private List<String> vegetables;
private List<String> sauces;
private Burger(Builder builder) {
this.bread = builder.bread;
this.patty = builder.patty;
this.vegetables = builder.vegetables;
this.sauces = builder.sauces;
}
@Override
public String toString() {
return "Burger{" +
"bread='" + bread + '\'' +
", patty='" + patty + '\'' +
", vegetables=" + vegetables +
", sauces=" + sauces +
'}';
}
public static class Builder {
private String bread;
private String patty;
private List<String> vegetables = new ArrayList<>();
private List<String> sauces = new ArrayList<>();
public Builder setBread(String bread) {
this.bread = bread;
return this;
}
public Builder setPatty(String patty) {
this.patty = patty;
return this;
}
public Builder addVegetable(String vegetable) {
this.vegetables.add(vegetable);
return this;
}
public Builder addSauce(String sauce) {
this.sauces.add(sauce);
return this;
}
public Burger build() {
return new Burger(this);
}
}
}
Director
指挥者,定义了调用构造步骤的顺序,封装了建造的过程,确保汉堡各个部分按正确的顺序创建。
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Burger construct() {
return builder.setBread("Sesame")
.setPatty("Beef")
.addVegetable("Lettuce")
.addVegetable("Tomato")
.addSauce("Ketchup")
.build();
}
}
public static void main(String[] args) {
Builder builder = new BurgerBuilder();
Director director = new Director(builder);
Burger burger = director.construct();
System.out.println(burger);
}

1.2.2 上述方案优点
- 清晰的API:通过链式调用,创建对象时更清晰、可读。
- 灵活性:可以选择性地设置属性,构建复杂对象时更灵活。
- 可维护性:新增属性时,不需要修改现有的构造函数,可以在建造者中添加新的设置方法。
- 简化代码:减少了参数列表的复杂性,使代码更易于管理和理解。
2. 建造者模式
2.1 定义
建造者模式/生成器模式/Builder
:用来创建一个对象,将一个复杂对象的创建与其表示分离,使用者不需要关心对象创建的流程,只需要知道一个接口就可以一步步构造一个比较复杂的对象。
可以理解为工人将许许多多的零部件拼凑在一起,搭建成一个完整的产品。
2.2 角色及结构
2.2.1 角色
-
Product
产品类:要创建的目标对象,如:汉堡。 -
Builder
抽象建造者:为创建一个Product对象的各个部分指定的抽象接口。如:添加蔬菜,酱料等的方法。 -
ConcreteBuilder
具体建造者:实现Builder
接口,构造和装配各个部件。如:具体的汉堡,具体实现如何构造出汉堡的各个部分。 -
Director
指挥者类:构建一个使用Builder
接口的对象。如:根据用户的需求创建想要的汉堡。
2.2.2 结构
2.3 适用场景
用于创建复杂对象,且对象内部构造的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化,可以理解为:构造过程一样,但是每个过程的实现细节不一样。
【例】 如:画一个小人,画一个小人的步骤可以概括为:
- 画头
- 画身体
- 画手
- 画脚
但是画胖小人和瘦小人的实现细节就不一样了。
对于用户来说,不想关注实现细节。只需要指定需要构造的类型即可,无需自己设置参数。
如:用户指定构建一个胖小人,直接指定胖,那么直接生成一个胖小人,无需关注身体、手、脚等的参数。
2.4 优缺点
2.4.1 优点
-
符合单一职责原则:隔离复杂对象的创建和使用,客户端无需关心对象创建细节。
-
更加灵活:可以分步创建对象,构建过程更加灵活。
-
符合开闭原则:增加新的具体
Builder
很方便,可以扩展Builder
功能。
2.4.2 缺点
代码复杂度会增加。
参考资料
转载自:https://juejin.cn/post/7408391774635950132