定义
建造者模式(Builder):将一个对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用建造者模式。
实现
案例:
外卖套餐
外卖套餐里面有必点,选点,然后各个菜品可以随意组合
必点:米饭、餐具
选点:小炒肉、土豆丝、麻婆豆腐。。。
public class WaiMai {
private String rice;
private String chopsticks;
private String xcr;
private String tds;
private String mpdf;
public WaiMai(String rice, String chopsticks) {
this.rice = rice;
this.chopsticks = chopsticks;
}
public WaiMai(String rice, String chopsticks, String xcr) {
this.rice = rice;
this.chopsticks = chopsticks;
this.xcr = xcr;
}
public WaiMai(String rice, String chopsticks, String xcr, String tds, String mpdf) {
this.rice = rice;
this.chopsticks = chopsticks;
this.xcr = xcr;
this.tds = tds;
this.mpdf = mpdf;
}
//方式一:通过构造函数来组合
//... 随着菜品的增多,这些组合是无限多的。那么这种构造函数也就无限多了,想想都不想写代码了。
//方式二:除了必点的使用构造函数,其他的都是 set 方法
// 虽然 set 相对来说,代码少了很多,但是 set 太随意了,你压根不知道这个套餐里面有没有这个菜,那么可能就会出错。
public void setXcr(String xcr) {
this.xcr = xcr;
}
public void setTds(String tds) {
this.tds = tds;
}
public void setMpdf(String mpdf) {
this.mpdf = mpdf;
}
}
所以,为了解决上面两种问题,建造者模式就出现了。
实战
产品:外卖
public class WaiMai {
private String rice;//米饭
private String chopsticks;//筷子
private String xcr;//小炒肉
private String tds;//土豆丝
private String mpdf;//麻婆豆腐
public WaiMai(String rice, String chopsticks) {
this.rice = rice;
this.chopsticks = chopsticks;
}
public void setXcr(String xcr) {
this.xcr = xcr;
}
public void setTds(String tds) {
this.tds = tds;
}
public void setMpdf(String mpdf) {
this.mpdf = mpdf;
}
@Override
public String toString() {
return "WaiMai{" +
"rice='" + rice + '\'' +
", chopsticks='" + chopsticks + '\'' +
", xcr='" + xcr + '\'' +
", tds='" + tds + '\'' +
", mpdf='" + mpdf + '\'' +
'}';
}
}
Builder
定义一个接口,表明需要建造什么,得到什么
public interface WaiMaiBuilder {
void setXcr();
void setTds();
void setMpdf();
WaiMai getWaiMai();
}
ConcreteBuilder
此时还没有生产套餐,只是定义套餐(下面只是使用一个套餐A,其他套餐同理)
public class WaiMaiConcreteBuilderA implements WaiMaiBuilder {
private WaiMai waiMai;
//因为这个是产品必须的,所以这里也必须有
public WaiMaiConcreteBuilderA(String rice,String chopstick){
waiMai = new WaiMai(rice,chopstick);
}
@Override
public void setXcr() {
waiMai.setXcr("小炒肉");
}
@Override
public void setTds() {
waiMai.setTds("土豆丝");
}
@Override
public void setMpdf() {
waiMai.setMpdf(null);
System.out.println("套餐A没有麻婆豆腐");
}
@Override
public WaiMai getWaiMai() {
return waiMai;
}
}
Director
真正的执行者,就像你自己在点餐的时候一样
public class Director {
public WaiMai build(WaiMaiBuilder builder){
builder.setMpdf();
builder.setTds();
builder.setXcr();
return builder.getWaiMai();
}
}
测试类
public class BuilderTest {
public static void main(String[] args) {
//必点品
WaiMaiBuilder builderA=new WaiMaiConcreteBuilderA("米饭","筷子");
//执行者创建产品,并不了解产品的细节
WaiMai waiMai=new Director().build(builderA);
System.out.println(waiMai);
}
}
//打印结果:
套餐A没有麻婆豆腐
WaiMai{rice='米饭', chopsticks='筷子', xcr='小炒肉', tds='土豆丝', mpdf='null'}
那么到这里,一个 “标准” 的建造者模式就实现了出来,如下UML图所示:
简化版
//实体类
public class WaiMai {
private String rice;
private String chopsticks;
private String xcr;
private String tds;
private String mpdf;
public WaiMai(WaiMaiBuilder waiMaiBuilder) {
rice = waiMaiBuilder.rice;
chopsticks = waiMaiBuilder.chopsticks;
xcr = waiMaiBuilder.xcr;
tds = waiMaiBuilder.tds;
mpdf = waiMaiBuilder.mpdf;
}
@Override
public String toString() {
return "WaiMai{" +
"rice='" + rice + '\'' +
", chopsticks='" + chopsticks + '\'' +
", xcr='" + xcr + '\'' +
", tds='" + tds + '\'' +
", mpdf='" + mpdf + '\'' +
'}';
}
// 静态内部类
public static class WaiMaiBuilder {
private String rice;
private String chopsticks;
private String xcr;
private String tds;
private String mpdf;
public WaiMaiBuilder(String rice,String chopstick) {
this.rice=rice;
chopsticks=chopstick;
}
public WaiMaiBuilder setXcr() {
this.xcr="小炒肉";
return this;
}
public WaiMaiBuilder setTds() {
this.tds="土豆丝";
return this;
}
public WaiMaiBuilder setMpdf() {
this.mpdf="麻婆豆腐";
return this;
}
public WaiMai getWaiMai() {
return new WaiMai(this);
}
}
}
// 测试
public class BuilderTest {
public static void main(String[] args) {
WaiMai waiMai=new WaiMai.WaiMaiBuilder("米饭","筷子").setMpdf().setXcr().getWaiMai();
System.out.println(waiMai);
}
}
Lombok-builder
@Builder(toBuilder = true)
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class WaiMai {
private String rice;
private String chopsticks;
private String xcr;
private String tds;
private String mpdf;
public WaiMai(String rice, String chopsticks) {
this.rice = rice;
this.chopsticks = chopsticks;
}
}
// 测试
public class BuilderTest {
public static void main(String[] args) {
WaiMai waiMai= new WaiMai("米饭","筷子").toBuilder().mpdf("麻婆豆腐").build();
System.out.println(waiMai);
}
}
WaiMai.class 文件:
public class WaiMai {
private String rice;
private String chopsticks;
private String xcr;
private String tds;
private String mpdf;
public WaiMai(String rice, String chopsticks) {
this.rice = rice;
this.chopsticks = chopsticks;
}
public static WaiMai.WaiMaiBuilder builder() {
return new WaiMai.WaiMaiBuilder();
}
public WaiMai.WaiMaiBuilder toBuilder() {
return (new WaiMai.WaiMaiBuilder()).rice(this.rice).chopsticks(this.chopsticks).xcr(this.xcr).tds(this.tds).mpdf(this.mpdf);
}
public String toString() {
return "WaiMai(rice=" + this.rice + ", chopsticks=" + this.chopsticks + ", xcr=" + this.xcr + ", tds=" + this.tds + ", mpdf=" + this.mpdf + ")";
}
public WaiMai() {
}
@ConstructorProperties({"rice", "chopsticks", "xcr", "tds", "mpdf"})
public WaiMai(String rice, String chopsticks, String xcr, String tds, String mpdf) {
this.rice = rice;
this.chopsticks = chopsticks;
this.xcr = xcr;
this.tds = tds;
this.mpdf = mpdf;
}
public static class WaiMaiBuilder {
private String rice;
private String chopsticks;
private String xcr;
private String tds;
private String mpdf;
WaiMaiBuilder() {
}
public WaiMai.WaiMaiBuilder rice(String rice) {
this.rice = rice;
return this;
}
public WaiMai.WaiMaiBuilder chopsticks(String chopsticks) {
this.chopsticks = chopsticks;
return this;
}
public WaiMai.WaiMaiBuilder xcr(String xcr) {
this.xcr = xcr;
return this;
}
public WaiMai.WaiMaiBuilder tds(String tds) {
this.tds = tds;
return this;
}
public WaiMai.WaiMaiBuilder mpdf(String mpdf) {
this.mpdf = mpdf;
return this;
}
public WaiMai build() {
return new WaiMai(this.rice, this.chopsticks, this.xcr, this.tds, this.mpdf);
}
public String toString() {
return "WaiMai.WaiMaiBuilder(rice=" + this.rice + ", chopsticks=" + this.chopsticks + ", xcr=" + this.xcr + ", tds=" + this.tds + ", mpdf=" + this.mpdf + ")";
}
}
}
总结
好处:即最开始定义所说的:建造代码与表示分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了(像我们上面的套餐A ,想要套餐B,只需要再定义一个套餐B即可)。
缺点:产品得组成部分必须相同,比如上面得必须品,在这里虽然很合逻辑,但是限制了使用范围
产品内部发生变化,建造者也要同步修改,后期维护成本大