昨天回顾学习了工厂模式。工厂模式是给用户提供一个创建对象的简单方法,使用户不用关心其创建过程的具体细节。今天的建造者模式也同样是这个理念,不同的是,工厂模式一般创建的是内部结构比较简单,初始化比较复杂的类,比如打造零件;而建造者模式是用来创建比较复杂的类,内部有很多成员的类,比如一辆汽车,他包括了很多种零件。
一、定义
建造者模式:将一个复杂对象的构建于它的表示分离,使得同样的构建过程可以创建不同的表示。
定义依然晦涩难懂。还是用生活中的例子举例。
你高兴地来到4s店准备买下一辆法拉利,选好车型后工作人员带你来到一个小房间告诉你要选配。于是你选了V12发动机,红色车身, 赛车方向盘,真皮座椅和最新的曲面液晶仪表盘。在这个过程中,你不需要知道这些配件是如何装到你的法拉利上的,但你最终会得到一辆你要求的法拉利。这个过程就是建造者模式。具体选配的安装,由具体的建造者来负责。
二、结构
一般的建造者模式由4个角色构成:
-
抽象建造者(Builder):它为产品对象的各个部件指定抽象接口。
-
具体建造者(ConcreteBuilder):它是抽象建造者的实现。
-
产品(Product):它是建造者最终要建造出来的类,
-
指挥者(Director):它负责指挥建造者。
三、代码实现
现在我们就来模拟一下买车的过程。
首先需要一个产品类Car
public class Car {
// 发动机
private String engine;
// 方向盘
private String steeringWheel;
// 座椅
private String chair;
// 仪表盘
private String dashboard;
// 车身颜色
private String color;
// 省略get和set方法
@Override
public String toString() {
return "Car{" +
"engine='" + engine + '\'' +
", steeringWheel='" + steeringWheel + '\'' +
", chair='" + chair + '\'' +
", dashboard='" + dashboard + '\'' +
", color='" + color + '\'' +
'}';
}
}
然后来一个抽象建造者
public abstract class CarBuilder {
Car mCar;
public CarBuilder() {
mCar = new Car();
}
public abstract void buildEngine(String engine);
public abstract void buildSteeringWheel(String steeringWheel);
public abstract void buildChair(String chair);
public abstract void buildDashboard(String dashboard);
public abstract void buildColor(String color);
public Car build() {
return mCar;
}
}
为什么需要抽象建造者?因为有不同的汽车厂商,他们对不同部件的创建方法有所不同。比如现在有法拉利和保时捷可选。
public class FerrariBuilder extends CarBuilder{
@Override
public void buildEngine(String engine) {
System.out.println("用法拉利的方式安装引擎");
mCar.setEngine(engine);
}
@Override
public void buildSteeringWheel(String steeringWheel) {
System.out.println("用法拉利的方式安装方向盘");
mCar.setSteeringWheel(steeringWheel);
}
@Override
public void buildChair(String chair) {
System.out.println("用法拉利的方式安装座椅");
mCar.setChair(chair);
}
@Override
public void buildDashboard(String dashboard) {
System.out.println("用法拉利的方式安装仪表盘");
mCar.setDashboard(dashboard);
}
@Override
public void buildColor(String color) {
System.out.println("用法拉利的方式喷漆");
mCar.setColor(color);
}
}
public class PorscheBuilder extends CarBuilder{
@Override
public void buildEngine(String engine) {
System.out.println("用保时捷的方式安装引擎");
mCar.setEngine(engine);
}
@Override
public void buildSteeringWheel(String steeringWheel) {
System.out.println("用保时捷的方式安装方向盘");
mCar.setSteeringWheel(steeringWheel);
}
@Override
public void buildChair(String chair) {
System.out.println("用保时捷的方式安装座椅");
mCar.setChair(chair);
}
@Override
public void buildDashboard(String dashboard) {
System.out.println("用保时捷的方式安装仪表盘");
mCar.setDashboard(dashboard);
}
@Override
public void buildColor(String color) {
System.out.println("用保时捷的方式喷漆");
mCar.setColor(color);
}
}
然后我们还需要一个指挥者来指挥他们造车
public class Direct {
private CarBuilder mBuilder;
public Direct(CarBuilder builder) {
this.mBuilder = builder;
}
public Car createCar(String engine, String steeringWheel, String chair, String dashboard, String color) {
mBuilder.buildEngine(engine);
mBuilder.buildSteeringWheel(steeringWheel);
mBuilder.buildChair(chair);
mBuilder.buildColor(color);
mBuilder.buildDashboard(dashboard);
return mBuilder.build();
}
}
最终用户来买车,只需要告诉自己的需求,就能得到一辆装好的车。
public class Client {
public static void main(String[] args) {
Direct direct = new Direct(new FerrariBuilder());
Car car = direct.createCar(
"V12", "赛车方向盘", "真皮座椅", "曲屏液晶仪表盘", "红色");
System.out.println(car.toString());
}
}
最终的运行结果:
省略Director
Director的作用是告诉建造者应该怎么建造,以什么样的顺序建造。所以如果对于建造顺序有一定的要求,就需要有一个Director来负责。但是如果没有顺序要求,那我们可以省略掉Director这个角色,使代码更精简。
这时我们先修改一下产品类,一般产品都会有默认的属性.
public class Car {
// 发动机
private String engine = "默认发动机";
// 方向盘
private String steeringWheel = "默认方向盘";
// 座椅
private String chair = "默认座椅";
// 仪表盘
private String dashboard = "默认仪表盘";
// 车身颜色
private String color = "默认颜色";
...
}
然后修改CarBuilder
public abstract class CarBuilder {
Car mCar = new Car();
public abstract CarBuilder buildEngine(String engine);
public abstract CarBuilder buildSteeringWheel(String steeringWheel);
public abstract CarBuilder buildChair(String chair);
public abstract CarBuilder buildDashboard(String dashboard);
public abstract CarBuilder buildColor(String color);
public Car build() {
return mCar;
}
}
注意这里我们将抽象方法的返回值改成了CarBuilder,这是省略Director的关键。
然后修改具体的建造者
public class PorscheBuilder extends CarBuilder {
@Override
public CarBuilder buildEngine(String engine) {
System.out.println("用保时捷的方式安装引擎");
mCar.setEngine(engine);
return this;
}
@Override
public CarBuilder buildSteeringWheel(String steeringWheel) {
System.out.println("用保时捷的方式安装方向盘");
mCar.setSteeringWheel(steeringWheel);
return this;
}
@Override
public CarBuilder buildChair(String chair) {
System.out.println("用保时捷的方式安装座椅");
mCar.setChair(chair);
return this;
}
@Override
public CarBuilder buildDashboard(String dashboard) {
System.out.println("用保时捷的方式安装仪表盘");
mCar.setDashboard(dashboard);
return this;
}
@Override
public CarBuilder buildColor(String color) {
System.out.println("用保时捷的方式喷漆");
mCar.setColor(color);
return this;
}
}
这里的每一次buildXXX,我们都返回了Buidler本身。
最终用户就可以这样使用
public class Client {
public static void main(String[] args) {
CarBuilder builder = new PorscheBuilder();
Car car = builder.buildEngine("V12发动机")
.buildSteeringWheel("赛车方向盘")
.buildColor("红色")
.buildChair("真皮座椅")
.buildDashboard("曲面液晶仪表盘")
.build();
System.out.println(car.toString());
}
}
这也就是链式编程。省略Director也是目前使用的比较多的。
运行效果如下:
四、特点
-
建造者模式相比较于工厂模式,它能够创建一个复杂的对象,并且用户不需要知道其创建的细节。并且每一个具体的建造者都相对独立,要增加新的建造者无需修改原本的代码,符合开闭原则。
-
建造者模式的缺点在于如果产品内部变化复杂,可能需要定义很多的建造者,会使系统变得庞大。
五、适用环境
-
当需要生产的对象有复杂的内部结构,而用户只用指定类型而无需知道创建细节的情况。
-
生产对象内部有一定顺序要求的情况,可以使用Director来限定创建顺序。