一个打工人的一天
早晨,早高峰,一路突突突到公司,还好没迟到。一上午,小曾噼里啪啦地敲键盘,早上忘记吃早餐,肚子好饿,终于扛到中午。
于是乎,一路奔赴食堂。食堂窗口有10来个菜,不过大多数人是点套餐,因为划得来。一荤一素一汤,好吃不贵。点好套餐后,选择自己喜欢吃的菜,就找个空位,开始就餐。
小曾边吃饭边想:是不是可以简化这个点餐过程呢?
顾客自助,根据套餐:一荤一素一汤,自己打菜。
定义一荤一素一汤的A套餐:
/**
* A套餐
* @author zherop
*/
public class SubMealA {
private String meat; // 肉类
private String vegetable; // 蔬菜类
private String drink; // 饮品类
public String getMeat() {
return meat;
}
public void setMeat(String meat) {
this.meat = meat;
}
public String getVegetable() {
return vegetable;
}
public void setVegetable(String vegetable) {
this.vegetable = vegetable;
}
public String getDrink() {
return drink;
}
public void setDrink(String drink) {
this.drink = drink;
}
@Override
public String toString() {
return "SubMealA [meat=" + meat + ", vegetable=" + vegetable + ", drink=" + drink + "]";
}
}
场景类:
/**
* 场景类
*
* @author zherop
*/
public class Client {
public static void main(String[] args) {
SubMealA subMealA = new SubMealA();
subMealA.setMeat("青椒炒肉");
subMealA.setVegetable("生菜");
subMealA.setDrink("紫菜蛋汤");
System.out.println(subMealA);
}
}
// SubMealA [meat=青椒炒肉, vegetable=生菜, drink=紫菜蛋汤]
顾客想要A套餐,然后根据A套餐所包含的3部分,分别在每一类可选的范围内,进行选择。什么都要知道,是不是感觉吃个饭怎么就这么难啊。
为了改进这个过程,打菜阿姨登场。
/**
* A套餐建造者,就是打菜的阿姨
* @author zherop
*/
public class SubMealABuilder {
private SubMealA subMealA;
public SubMealABuilder() {
this.subMealA = new SubMealA();
}
private void buildMeat() {
subMealA.setMeat("青椒炒肉");
}
private void buildVegetable() {
subMealA.setVegetable("生菜");
}
private void buildDrink() {
subMealA.setDrink("紫菜蛋汤");
}
public void build() {
buildMeat();
buildVegetable();
buildDrink();
}
public SubMealA getSubMealA() {
return subMealA;
}
}
场景类变成了这样:
/**
* 场景类
*
* @author zherop
*/
public class Client {
public static void main(String[] args) {
SubMealABuilder subMealABuilder = new SubMealABuilder();
subMealABuilder.build();
SubMealA subMealA = subMealABuilder.getSubMealA();
System.out.println(subMealA);
}
}
// SubMealA [meat=青椒炒肉, vegetable=生菜, drink=紫菜蛋汤]
顾客不用再关心套餐里的东西怎么来的,只需要找到负责A套餐的打菜阿姨就行。
但是老是吃这几个菜,一荤一素一汤套餐,想换其他类别的怎么办呢?
既然A套餐,包含多种组合,那么定义一个A套餐的建造规范:
/**
* A套餐建造规范
* @author zherop
*/
public abstract class AbstractSubMealABuilder {
protected SubMealA subMealA;
public AbstractSubMealABuilder() {
this.subMealA = new SubMealA();
}
protected abstract void buildMeat();
protected abstract void buildVegetable();
protected abstract void buildDrink();
public void build() {
buildMeat();
buildVegetable();
buildDrink();
}
public SubMealA getSubMealA() {
return subMealA;
}
}
A套餐的有两个负责打菜的阿姨:
/**
* 负责A套餐1号的阿姨
*
* @author zherop
*
*/
public class SubMealA1Builder extends AbstractSubMealABuilder {
@Override
protected void buildMeat() {
this.subMealA.setMeat("青椒炒肉");
}
@Override
protected void buildVegetable() {
this.subMealA.setVegetable("生菜");
}
@Override
protected void buildDrink() {
this.subMealA.setDrink("紫菜蛋汤");
}
}
/**
* 负责A套餐2号的阿姨
* @author zherop
*/
public class SubMealA2Builder extends AbstractSubMealABuilder {
@Override
protected void buildMeat() {
this.subMealA.setMeat("孜然牛肉");
}
@Override
protected void buildVegetable() {
this.subMealA.setVegetable("空心菜");
}
@Override
protected void buildDrink() {
this.subMealA.setDrink("冬瓜排骨汤");
}
}
场景类变成了这样:
/**
* 场景类
*
* @author zherop
*/
public class Client {
public static void main(String[] args) {
// 选择A套餐1号
// AbstractSubMealABuilder subMealABuilder = new SubMealA1Builder();
// 选择A套餐2号
AbstractSubMealABuilder subMealABuilder = new SubMealA2Builder();
subMealABuilder.build();
SubMealA subMealA = subMealABuilder.getSubMealA();
System.out.println(subMealA);
}
}
// SubMealA [meat=孜然牛肉, vegetable=空心菜, drink=冬瓜排骨汤]
这个时候,想要A套餐1号菜,就找1号打菜阿姨,想要A套餐2号菜,就找2号打菜的阿姨就行。
似乎一些都还不错,可能是已经习惯了,但是习惯的东西不见得合理。现在想吃什么套餐,需要拿着空餐盘,去找对应的阿姨打菜,等他们打好菜(subMealABuilder.build()),再把餐盘给我们,必须要先打好菜,再把餐盘给我们才有意义。
我们再来改进下,这个时候小姐姐(前台服务员)登场。
/**
* 前台服务员
*
* @author zherop
*
*/
public class FrontWaiter {
private AbstractSubMealABuilder subMealA1Builder;
public void setSubMealA1Builder(AbstractSubMealABuilder subMealA1Builder) {
this.subMealA1Builder = subMealA1Builder;
}
/**
* 获取A套餐
*
* @return
*/
public SubMealA getSubMealA1() {
subMealA1Builder.build();
return subMealA1Builder.getSubMealA();
}
}
这个时候场景类变成了这样:
/**
* 场景类
* @author zherop
*/
public class Client {
public static void main(String[] args) {
FrontWaiter frontWaiter = new FrontWaiter();
// 告诉服务员,我要点A套餐1号
frontWaiter.setSubMealA1Builder(new SubMealA1Builder());
// 告诉服务员,我要点A套餐2号
// frontWaiter.setSubMealA1Builder(new SubMealA2Builder());
SubMealA subMealA1 = frontWaiter.getSubMealA1();
System.out.println(subMealA1);
}
}
这下,顾客只需要跟前台服务员打交道了,再也不用拿着空餐盘,去让打菜阿姨打菜了。直接告诉前台服务员,顾客想要什么套餐,然后服务员就直接给了,不用再操心那么多了。如果后续A套餐还有其他系列,比如3号、4号…,直接新增对应的打菜阿姨,然后顾客直接告诉前台服务员需要的系列即可,而对于顾客来说整个过程没有什么变化。
好了,饭也吃完了,回公司。午休,敲键盘5小时候后,下班。
定义
建造者模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
涉及角色:
- 抽象建造者(Builder)
为创建一个产品对象的各个部件定义抽象接口 ,一般会有两类方法:一类是buildPartXXX(),由于创建各个部件,一类是方法是getResult(),返回复杂产品对象。抽象建造者可以是抽象类,也可以是接口。比如故事中的AbstractSubMealABuilder。 - 具体建造者(ConcreteBuilder)
实现抽象建造者定义的接口,即实现各个部件的具体创建。比如故事中的SubMealA1Builder、SubMealA2Builder - 产品(Product)
被构建的复杂对象,包含多个部件。比如故事中的SubMealA。 - 指挥者(Director)
负责安排复杂对象的建造次序,对客户端而言,不需要再关心各个部件创建的顺序。比如故事中的FrontWaiter。
类图:
适用场景
- 需要生成的产品对象有复杂的内部结构,通过包含多个成员属性。
- 需要生成的产品对象的属性相互依赖,需要指定各个属性的生成顺序。
- 隔离复杂对象的创建和使用。
扩展
- 如果具体建造者只有一个时,可以省略抽象建造者;
- 如果复杂对象的各个部件的生成顺序,关系不大,这个时候也可以考虑省略指挥者。