概述
将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。

- 分离了部件的构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况。
- 由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
- 建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。
结构:
建造者(Builder)模式由抽象建造者、具体建造者、****产品、导向器等 4 个要素构成。

一般端直接和Director导向器沟通,通过向Director传入不同的ConcreteBuilder(具体建造者)构建不同表示的Product(产品)。
所以建造者模式把对象的构建(由Builder的实现来负责的)和装配(由Director负责的)进行了解耦,不同的构建器,相同的装配,也可以做出不同的产品。
模式的优缺点
1)可以逐步构建对象、延迟构建步骤,将构建和表示分离;
2)各个具体的建造者相互独立,您可以重用相同的构建代码;
产品的组成部分必须相同,这限制了其使用范围,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。
创建的方式
首先创建抽象建造者;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| public interface Builder { void setTyre(Tyre tyre); void setSeat(Seat seat); void setEngine(Engine engine); }
class Tyre{ private String tyreVersion;
public Tyre(){ this.tyreVersion="入门级轮胎,寿命5年"; } public Tyre(String tyreVersion) { this.tyreVersion = tyreVersion; }
public String getTyreVersion() { return tyreVersion; } }
class Seat{ private String SeatVersion;
public Seat(){ this.SeatVersion="普通座椅"; } public Seat(String seatVersion) { SeatVersion = seatVersion; } public String getSeatVersion() { return SeatVersion; }
}
class Engine{ private String EngineVersion;
public Engine(){ this.EngineVersion="普通发动机"; } public Engine(String engineVersion) { EngineVersion = engineVersion; } public String getEngineVersion() { return EngineVersion; } }
|
然后再创建实际建造者及产品;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| public class BydBuilder implements Builder {
private Tyre tyre; private Seat seat; private Engine engine; @Override public void setEngine(Engine engine) { this.engine = engine; }
@Override public void setSeat(Seat seat) { this.seat = seat; }
@Override public void setTyre(Tyre tyre) { this.tyre = tyre; } public BydCar getMyBydCar(){ return new BydCar(tyre, seat, engine); } }
class BydCar{ private Tyre tyre; private Seat seat; private Engine engine; public BydCar(Tyre tyre,Seat seat,Engine engine){ this.tyre = tyre; this.seat = seat; this.engine = engine; System.out.println(this.toString()); } public Tyre getTyre() { return tyre; } public Seat getSeat() { return seat; } public Engine getEngine() { return engine; }
@Override public String toString() { return this.engine.getEngineVersion()+"+"+ this.tyre.getTyreVersion()+"+"+ this.seat.getSeatVersion(); } }
|
再创建导向器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Director {
public void highBydCar(Builder builder){ builder.setEngine(new Engine("高端发动机")); builder.setTyre(new Tyre("高端轮胎,可用10年")); builder.setSeat(new Seat("高端真皮座椅")); } public void lowBydCar(Builder builder){ builder.setEngine(new Engine()); builder.setTyre(new Tyre()); builder.setSeat(new Seat()); } public void MiddleBydCar(Builder builder){ builder.setEngine(new Engine("高端发动机")); builder.setTyre(new Tyre()); builder.setSeat(new Seat()); } }
|
最后就是我们的客户端调用者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class Client {
public static void main(String[] args) { Director director = new Director(); BydBuilder builder = new BydBuilder(); System.out.println("顾客1,看中了高端比亚迪"); director.highBydCar(builder); builder.getMyBydCar(); System.out.println("顾客2,看中了低端比亚迪"); director.lowBydCar(builder); builder.getMyBydCar(); System.out.println("顾客3,看中了中端比亚迪"); director.MiddleBydCar(builder); builder.getMyBydCar(); System.out.println("顾客4,希望按照自己的意愿定制比亚迪"); builder.setEngine(new Engine("高端发动机")); builder.setSeat(new Seat("高端座椅")); builder.setTyre(new Tyre()); builder.getMyBydCar(); } }
|
顾客1,看中了高端比亚迪
高端发动机+高端轮胎,可用10年+高端真皮座椅
顾客2,看中了低端比亚迪
普通发动机+入门级轮胎,寿命5年+普通座椅
顾客3,看中了中端比亚迪
高端发动机+入门级轮胎,寿命5年+普通座椅
顾客4,希望按照自己的意愿定制比亚迪
高端发动机+入门级轮胎,寿命5年+高端座椅
模式扩展
建造者模式除了上面的用途外,在开发中还有一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码可读性会非常差,而且很容易引入错误,此时就可以利用建造者模式进行重构。
重构前代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| public class Phone { private String cpu; private String screen; private String memory; private String mainboard;
public Phone(String cpu, String screen, String memory, String mainboard) { this.cpu = cpu; this.screen = screen; this.memory = memory; this.mainboard = mainboard; }
public String getCpu() { return cpu; }
public void setCpu(String cpu) { this.cpu = cpu; }
public String getScreen() { return screen; }
public void setScreen(String screen) { this.screen = screen; }
public String getMemory() { return memory; }
public void setMemory(String memory) { this.memory = memory; }
public String getMainboard() { return mainboard; }
public void setMainboard(String mainboard) { this.mainboard = mainboard; }
@Override public String toString() { return "Phone{" + "cpu='" + cpu + '\'' + ", screen='" + screen + '\'' + ", memory='" + memory + '\'' + ", mainboard='" + mainboard + '\'' + '}'; } }
public class Client { public static void main(String[] args) { Phone phone = new Phone("intel","三星屏幕","金士顿","华硕"); System.out.println(phone); } }
|
上面在客户端代码中构建Phone对象,传递了四个参数,如果参数更多呢?代码的可读性及使用的成本就是比较高。
重构后代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| public class Phone {
private String cpu; private String screen; private String memory; private String mainboard;
private Phone(Builder builder) { cpu = builder.cpu; screen = builder.screen; memory = builder.memory; mainboard = builder.mainboard; }
public static final class Builder { private String cpu; private String screen; private String memory; private String mainboard;
public Builder() {}
public Builder cpu(String val) { cpu = val; return this; } public Builder screen(String val) { screen = val; return this; } public Builder memory(String val) { memory = val; return this; } public Builder mainboard(String val) { mainboard = val; return this; } public Phone build() { return new Phone(this);} } @Override public String toString() { return "Phone{" + "cpu='" + cpu + '\'' + ", screen='" + screen + '\'' + ", memory='" + memory + '\'' + ", mainboard='" + mainboard + '\'' + '}'; } }
public class Client { public static void main(String[] args) { Phone phone = new Phone.Builder() .cpu("intel") .mainboard("华硕") .memory("金士顿") .screen("三星") .build(); System.out.println(phone); } }
|
重构后的代码在使用起来更方便,某种程度上也可以提高开发效率。从软件设计上,对程序员的要求比较高。
总结及建议
建造者模式实现了构建和装配的解耦,不同的构建器,相同的装配,可以做出不同的产品对象;相同的构建器不同的装配顺序也可以做出不同的产品对象。
应用场景:
- 相同的方法,不同的执行顺序,产生不同的结果。
- 产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。
- 初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。
DJK中建造者模式的使用:
- java.lang.StringBuilder#append() (unsynchronized)
- java.lang.StringBuffer#append() (synchronized)
- java.nio.ByteBuffer#put()
创建者模式对比
工厂方法模式VS建造者模式
工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。
我们举个简单例子来说明两者的差异,如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷、能够飞翔、内裤外穿的超人;而如果使用建造者模式,则需要组装手、头、脚、躯干等部分,然后再把内裤外穿,于是一个超人就诞生了。
抽象工厂模式VS建造者模式
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。