「行情不行,就得多努力」
建建:这场疫情啊,让我明白了,有一个房子,能让你稳定住所;有一辆车,能让你出行无忧。
子乾:听这话意思是要买房买车了呢。
建建:要不资助点?
子乾:那明天你跟我一起出门挣点。
建建:去哪,在哪个位置?
子乾:以前啊,就在那大商场门口就行,一个碗,一根棍,一个铺盖趴一天,天天收入好几百。现在行情不行了呀。
建建:还是给我爸打个电话吧,明天去提车。
子乾:那建总要宝马还是大奔,是保时捷还是劳斯莱斯?
建建:行吧,那我就要一辆“环环相扣”的吧。
那么问题来了,面对众多的车牌,从在厂生产,到 4S 店销售,再到客户手中,这样一个复杂“车”对象,购买和生产的来龙去脉怎么表示在代码里面?
那这就需要 「建造者模式」了。
什么是 建造者模式?(别名生成器模式)
Builder Pattern: Separate the construction of a complex object from its representation so that the same construction process can create different representations.
看不懂看下面:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
它是对象创建型模式。
先来看一下类图:
角色:
指挥者 Director
抽象建造者 Builder
具体建造者 SubBuilder1、SubBuilder2 ...
目标产品 Product
如果你想买一辆车,Product 是车,Builder 是抽象的车制造厂,SubBuilder1 、SubBuilder2 可以是宝马制造厂、红旗制造厂、特斯拉制造厂等等... Director 则可以是汽车销售员。
你只需要告诉销售员想要的车牌,背后经过一系列生产组装(不需你管),一辆完整的车就送到了你面前。
再比如,现在饭店都有套餐,你去了想吃一份套餐,Product 是一份套餐,Builder 是抽象的套餐制作机,SubBuilder1、SubBuilder2 可以是具体的某一种套餐的制作机,麻辣香锅焖面套餐带米饭雪碧、三荤三素带米饭可乐、五荤五素带米饭等等... Director 则可以是饭店服务员。
你只需要告诉服务员想要的套餐名字,大厨们一顿操作(无需你管),一份完整的套餐就送到了你面前。
我们以饭店实例做一下拆解:
类图:
目标产品 Meal 在制作类 SubMealBuilderA 或者 SubMealBuilderB 完成制作,而为了“开闭原则”,他们有共同的制作类父类 MealBuilder 类,这在客户端的调用过程中就用到了“里氏代换”原则。
服务员 Waiter 则通过制作类的父类,完成调用。实际 Waiter 中使用的具体制作过程函数,肯定是制作类子类的。因为具体实现都在子类呀。
客户和服务员打交道就可以了,只需要告诉服务员想要的具体产品。
下面通过代码来解析一下这个过程:
壹
首先,要有一个目标产品类 Meal:
它包含该产品的具体组成,这份套餐中包含食物(麻辣香锅焖面套餐带米饭、三荤三素带米饭...)、饮料(可乐、雪碧、脉动...)
代码语言:javascript复制package com.sample.buildpattern;
// 目标产品类
public class Meal {
private String food;
private String drink;
public String getFood() {
return food;
}
public void setFood(String food) {
this.food = food;
}
public String getDrink() {
return drink;
}
public void setDrink(String drink) {
this.drink = drink;
}
}
贰
然后,定义一个制作类父类 MealBuilder:
该类依赖于目标产品类,因为该系列的类负责制作完成一份完整的目标产品。
因此这里面要包含食物、饮料的制作过程,因为目标产品 Meal 定义为了 protected,所以还需要一个类将生成的目标产品外用,即 getMeal()。
代码语言:javascript复制package com.sample.buildpattern;
public abstract class MealBuilder {
protected Meal meal = new Meal();
public abstract void buildFood();
public abstract void buildDrink();
public abstract Meal getMeal();
}
叁
要定义 MealBuilder 具体的子类,每个子类负责制作不同的套餐。
SubMealBuilderA 负责制作麻辣香锅焖面套餐带米饭可乐套餐,SubMealBuilderB 负责制作三荤三素带米饭可乐套餐。
SubMealBuilderA:
代码语言:javascript复制package com.sample.buildpattern;
public class SubMealBuilderA extends MealBuilder{
@Override
public void buildFood() {
System.out.println("麻辣香锅焖面套餐加米饭");
}
@Override
public void buildDrink() {
System.out.println("雪碧");
}
}
SubMealBuilderB:
代码语言:javascript复制
public class SubMealBuilderB extends MealBuilder {
@Override
public void buildFood() {
System.out.println("三荤三素加米饭");
}
@Override
public void buildDrink() {
System.out.println("可乐");
}
}
肆
定义指挥者服务员类 Waiter。
它负责调用(告诉)制作人员,客户需要哪一款套餐。
因此该类需要聚合制作类父类,手里握着制作类们的父亲,儿子们不敢不听话,想使用哪个儿子制作产品就可以调用哪一个。
制作类的父类为 Waiter 类私有属性,通过 set 方法为其赋值,并且在 construct 方法中,获得制作类制作的产品。
代码语言:javascript复制package com.sample.buildpattern;
public class Waiter {
private MealBuilder mealBuilder;
public void setMealBuilder(MealBuilder mealBuilder){
this.mealBuilder = mealBuilder;
}
public Meal construct(){
mealBuilder.buildFood();
mealBuilder.buildDrink();
return mealBuilder.getMeal();
}
}
伍
完成客户端类,客户端用户直接和服务员类 Waiter 打交道,告诉服务员需要什么套餐,服务员进行安排后,用户拿到目标套餐。
用户想要麻辣香锅焖面套餐加米饭可乐,因此指定该套餐,服务员收到后传达,制作人员一顿操作,用户拿到 meal,可以输出看是不是想要的套餐。
代码语言:javascript复制package com.sample.buildpattern;
public class Client {
public static void main(String[] args){
// 指定套餐
MealBuilder mealBuilder = new SubMealBuilderA();
//服务员传达
Waiter waiter = new Waiter();
waiter.setMealBuilder(mealBuilder);
//返回食品
Meal meal = waiter.construct();
meal.getDrink();
meal.getFood();
}
}
陆
结果:完美~
以上就是该模式的一个细致拆分分享。
通过以上分析,隐隐约约可以感受到该模式的一些优缺点。类挺多,开闭原则好像也符合,对吧。
下面我们总结一下它的优缺点:
优点:
▏客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使相同的创建过程可以创建不同的产品对象;
▏建造者类符合开闭原则,每个独立,很方便的替换、新增、删除;
▏可以更加精细的控制产品的创建过程。
缺点:
▏建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,不适合使用建造者模式;
▏产品内部变化复杂,可能需要创建很多具体的建造者,系统变的庞大臃肿复杂。
建造者模式适用的环境:
☆ 需要生成的产品对象有复杂的内部结构,比如多个成员变量,而且还是引用类型变量;
☆ 需要生成的产品对象的属性相互依赖,需要指定生成顺序;
☆ 对象的创建过程独立于创建该对象的类。创建过程在指挥者类中,不在建造者类,也不在客户端类;
☆ 复杂对象的创建和使用满足隔离要求。
诶,有没有一个小疑惑。
建造者模式是:告诉它,想要什么,就可以给你返回什么。
而抽象工厂模式也是,想要什么,告诉它,就可以给你返回什么。
它们俩有哪些区别呢?
从字面理解,工厂就是生产产品,生产某一样产品。建造者模式是构建,构建一件产品。
▌建造者模式返回的是一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成一个产品族。回顾这个:设计模式 - 抽象工厂模式;
▌抽象工厂模式中,客户端实例化工厂类,调用工厂方法,获得目标产品。而建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者来引导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整复杂对象。
▌若将抽象工厂模式认为是汽车配件生产工厂,生产一个产品族的产品,轮胎、方向盘、发动机等。那么建造者模式就是汽车组装工厂,通过对部件的组装返回一辆完整汽车,宝马、奔驰、保时捷。
亲爱的读者朋友,不知不觉今天的分享就又结束了。你有什么想和我说的吗?
感谢陪伴,感谢阅读。
设计模式相关 demo 代码,在这个码云链接:
https://gitee.com/JeffBro/DesignPattern23
表情包来源于网络,侵删。
「子乾建建为作者笔名」