简述
建造者模式有点像工厂模式,不过工厂模式对外并不展现产品的制造过程,建造者模式却是将产品的装配方法暴露给了使用者,使用者可以通过自行调用这些装配方法、策划调用的次序,来在一定程度上影响产品的制造结果。
Runoob上给出了一个在KFC点餐的例子,即用户可以点汉堡、薯条、鸡翅等,但是点这些餐品的顺序不同,所生成的产品(倒不如说是副产品)——订单条也就不太一样。
程序
我将这个过程简化成了一个比Runoob上更清晰的程序。
SnacksBuilder接口
package org.lzh.builder;
import org.lzh.enumeration.Size;
//小吃的建造者接口
public interface SnacksBuilder {
void buildFrenchFries(Size size);//加个薯条
void buildCoke(Size size);//加个可乐
}
StapleBuilder接口
package org.lzh.builder;
import org.lzh.enumeration.Taste;
//主食的建造者接口
public interface StapleBuilder {
void buildHamburger(Taste taste);//加个汉堡
void buildChickenRoll();//加个鸡肉卷
}
KFCBuilder建造者类
package org.lzh.builder.imp;
import org.lzh.builder.SnacksBuilder;
import org.lzh.builder.StapleBuilder;
import org.lzh.enumeration.Size;
import org.lzh.enumeration.Taste;
import java.util.LinkedList;
import java.util.List;
//模拟KFC点餐的建造者实体类,可以点主食和小吃
public class KFCBuilder implements StapleBuilder, SnacksBuilder {
//订单
private List<String> order = new LinkedList<>();
@Override
public void buildFrenchFries(Size size) {
order.add((size == Size.LARGE ? "大" : size == Size.MID ? "中" : "小") + "薯条");
}
@Override
public void buildCoke(Size size) {
order.add((size == Size.LARGE ? "大" : size == Size.MID ? "中" : "小") + "杯可乐");
}
@Override
public void buildHamburger(Taste taste) {
order.add((taste == Taste.SPICY ? "香辣" : "劲脆") + "鸡腿堡");
}
@Override
public void buildChickenRoll() {
order.add("老北京鸡肉卷");
}
//获取订单
public List<String> getOrder() {
return order;
}
//展示订单
public void showOrder() {
for (String s : this.order) {
System.out.println("|---" + s);
}
}
}
Main场景类
package org.lzh;
import org.lzh.builder.imp.KFCBuilder;
import org.lzh.enumeration.Size;
import org.lzh.enumeration.Taste;
public class Main {
public static void main(String[] args) {
KFCBuilder kfcBuilder = new KFCBuilder();
kfcBuilder.buildChickenRoll();
kfcBuilder.buildCoke(Size.LITTLE);
kfcBuilder.buildFrenchFries(Size.LARGE);
kfcBuilder.buildHamburger(Taste.NOTSPICY);
kfcBuilder.showOrder();
}
}
运行结果
|---老北京鸡肉卷
|---小杯可乐
|---大薯条
|---劲脆鸡腿堡
StringBuilder
StringBuilder
是字符串的一个建造者类,我认为它是说明建造者模式的一个绝佳的例子。它继承了一个抽象的建造者类,并像String
一样用final
修饰不允许被继承:
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
在抽象的AbstractStringBuilder
类中,可以看到很多和建造字符串有关的方法,如:
public void setCharAt(int index, char ch){
//...
}
public AbstractStringBuilder append(/*各种类型的参数*/){
//...
}
public AbstractStringBuilder delete(int start, int end){
//...
}
public AbstractStringBuilder deleteCharAt(int index){
//...
}
public AbstractStringBuilder replace(int start, int end, String str){
//...
}
//还有很多...
其中以AbstractStringBuilder
为返回值的方法,皆尽返回this
,可见它们都是用来建造字符串的方法,并且可以方便地做链式调用。StringBuilder
维护了一个缓冲区,使用者通过组合这些方法,并传递不同的参数,就可以用来构建各式各样的字符串了,这正是使用了建造者模式。
这个类的存在让字符串的构造更加灵活。在这里,最终生成的各式各样的字符串就是产品,而这些产品的建造过程是可以抽象成几种步骤的,通过这些步骤的组合能更灵活方便地建造这些产品,这就是建造者模式的使用情景——定制化。
建造者模式的链式调用
前面的例子用起来极为不方便,建造者模式一般都做成链式调用的,可以让建造过程的方法返回this
,这样就能链式地调用建造过程,最后调用一个build()
方法来返回产品对象。
比如,最近看的《Spring实战》上提到的EmbeddedDatabaseBuilder
创建嵌入式的Hypersonic
数据库就是一直add*()
最后build()
的链式调用。