本文已参与「新人创作礼」活动,一起开启掘金创作之路。
看个图放松一下
从度娘上找了一个动图,很清楚的可以看到这是一条流水线。黄色衣服的工人负责将盒子放进袋子里,蓝色工人负责将袋子密封。流水线大家应该都清楚,就是每个人专门负责一道工序,一个完整的产品需要经过所有的流水线工人。这个现实中的例子刚好可以帮助大家理解建造者模式。
建造者模式
《大话设计模式》定义:建造者模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
初探设计模式的小伙伴可能不太理解这个定义,大家可以简单理解为,new就是将对象的构建与表示合在一起了,=左边的可以理解为表示,而=右边的new Xxx(a,b)就是生产过程了。所以建造者模式将表示与生产过程分离,这有两种好处:
- 一个是保证创建复杂对象时规定了生产步骤, 保证了创建出来的对象符合规则(下面案例会具体解释)
- 实现了产品构建过程的解耦(客户端不通过new来创建对象了 )
造车与造汽车手册案例
该案例是体现的是创建不同种类的汽车与不同种类的汽车手册。重要事情说三遍,这个案例是建造了两种类型的产品,更能体现出建造者模式的一些特点,看一遍你不一定能理解,建议看完代码,自己先对着敲一遍,然后对照下面的分析学习!
项目结构图:《深入设计模式》案例
实体类及汽车类型枚举:Car(汽车),Manual(汽车手册),CarType(汽车类型枚举)
建造者模式是创建型模式,最终的结果是创建出来对象,汽车(Car)与汽车手册(Manual)就是案例中建造者模式要创建的对象。汽车类型(CarType)是汽车(Car)创建时需要的一个属性。
// 跑车,城市汽车,SUV
public enum CarType {
CITY_CAR, SPORTS_CAR, SUV
}
public class Car {
// 汽车类型
private final CarType carType;
// 座位数量
private final int seats;
// 发动机
private final Engine engine;
// 变速箱
private final Transmission transmission;
// 行程仪
private final TripComputer tripComputer;
// 导航仪
private final GPSNavigator gpsNavigator;
// 燃料
private double fuel = 0;
public Car(CarType carType, int seats, Engine engine, Transmission transmission, TripComputer tripComputer, GPSNavigator gpsNavigator) {
this.carType = carType;
this.seats = seats;
this.engine = engine;
this.transmission = transmission;
this.tripComputer = tripComputer;
if (this.tripComputer != null) {
this.tripComputer.setCar(this);
}
this.gpsNavigator = gpsNavigator;
}
public CarType getCarType() {
return carType;
}
public double getFuel() {
return fuel;
}
public void setFuel(double fuel) {
this.fuel = fuel;
}
public int getSeats() {
return seats;
}
public Engine getEngine() {
return engine;
}
public Transmission getTransmission() {
return transmission;
}
public TripComputer getTripComputer() {
return tripComputer;
}
public GPSNavigator getGpsNavigator() {
return gpsNavigator;
}
}
public class Manual {
private final CarType carType;
private final int seats;
private final Engine engine;
private final Transmission transmission;
private final TripComputer tripComputer;
private final GPSNavigator gpsNavigator;
public Manual(CarType carType, int seats, Engine engine, Transmission transmission, TripComputer tripComputer, GPSNavigator gpsNavigator) {
this.carType = carType;
this.seats = seats;
this.engine = engine;
this.transmission = transmission;
this.tripComputer = tripComputer;
this.gpsNavigator = gpsNavigator;
}
public String print() {
String info = "";
info += "Type of car: " + carType + "\n";
info += "Count of seats: " + seats + "\n";
info += "Engine: volume - " + engine.getVolume() + "; mileage - " + engine.getMileage() + "\n";
info += "Transmission: " + transmission + "\n";
if (this.tripComputer != null) {
info += "Trip Computer: Functional" + "\n";
} else {
info += "Trip Computer: N/A" + "\n";
}
if (this.gpsNavigator != null) {
info += "GPS Navigator: Functional" + "\n";
} else {
info += "GPS Navigator: N/A" + "\n";
}
return info;
}
}
汽车组件:Engine(发动机),Transmission(变速箱),TripComputer (行程仪),GPSNavigator(导航仪)
这些类/枚举对应的是建造一辆汽车需要的一些组件。
public class Engine {
// 体积
private final double volume;
// 里程
private double mileage;
// 启动
private boolean started;
public Engine(double volume, double mileage) {
this.volume = volume;
this.mileage = mileage;
}
public void on() {
started = true;
}
public void off() {
started = false;
}
public boolean isStarted() {
return started;
}
public void go(double mileage) {
if (started) {
this.mileage += mileage;
} else {
System.out.println("Cannot go(), you must start engine first!");
}
}
public double getVolume() {
return volume;
}
public double getMileage() {
return mileage;
}
}
/**
* 变速箱 (单速,手动挡,自动挡,半自动)
*/
public enum Transmission {
SINGLE_SPEED, MANUAL, AUTOMATIC, SEMI_AUTOMATIC
}
public class TripComputer {
private Car car;
public void setCar(Car car) {
this.car = car;
}
public void showFueLevel() {
System.out.println("Fuel level:" + car.getFuel());
}
}
public class GPSNavigator {
// 路线
private String route;
public GPSNavigator() {
this.route = "221b, Baker Street, London to Scotland Yard, 8-10 Broadway, London";
}
public GPSNavigator(String manualRoute) {
this.route = manualRoute;
}
public String getRoute() {
return route;
}
}
抽象建造者与具体建造者:Builder(抽象建造接口),CarBuilder(具体建造者),CarManualBuilder(具体建造者)
- Builder:抽象建造接口,定义了建造一个车/手册的所有流程方法
- CarBuilder:汽车具体建造者,具体的汽车建造者,内部提供一个getResult()方法获取一个Car对象
- CarManualBuilder:汽车手册具体建造者,具体的手册建造者,内部提供一个getResult()方法获取一个Manual对象
public interface Builder {
void setCarType(CarType type); // 车型
void setSeats(int seats); // 座位
void setEngine(Engine engine); // 发动机
void setTransmission(Transmission transmission); // 变速箱
void setTripComputer(TripComputer tripComputer); // 行程表
void setGPSNavigator(GPSNavigator gpsNavigator); // 导航仪
}
public class CarBuilder implements Builder {
private CarType type;
private int seats;
private Engine engine;
private Transmission transmission;
private TripComputer tripComputer;
private GPSNavigator gpsNavigator;
@Override
public void setCarType(CarType type) {
this.type = type;
}
@Override
public void setSeats(int seats) {
this.seats = seats;
}
@Override
public void setEngine(Engine engine) {
this.engine = engine;
}
@Override
public void setTransmission(Transmission transmission) {
this.transmission = transmission;
}
@Override
public void setTripComputer(TripComputer tripComputer) {
this.tripComputer = tripComputer;
}
@Override
public void setGPSNavigator(GPSNavigator gpsNavigator) {
this.gpsNavigator = gpsNavigator;
}
public Car getResult() {
return new Car(type, seats, engine, transmission, tripComputer, gpsNavigator);
}
}
public class CarManualBuilder implements Builder {
private CarType type;
private int seats;
private Engine engine;
private Transmission transmission;
private TripComputer tripComputer;
private GPSNavigator gpsNavigator;
@Override
public void setCarType(CarType type) {
this.type = type;
}
@Override
public void setSeats(int seats) {
this.seats = seats;
}
@Override
public void setEngine(Engine engine) {
this.engine = engine;
}
@Override
public void setTransmission(Transmission transmission) {
this.transmission = transmission;
}
@Override
public void setTripComputer(TripComputer tripComputer) {
this.tripComputer = tripComputer;
}
@Override
public void setGPSNavigator(GPSNavigator gpsNavigator) {
this.gpsNavigator = gpsNavigator;
}
public Manual getResult(){
return new Manual(type, seats, engine, transmission, tripComputer, gpsNavigator);
}
}
主管控制生成器:Director (指挥者)
指挥者里定义了三个方法,可以很清楚的看到实际是对传入的建造者进行赋值,三个方法分别创建了不同的车/手册,具体是生产车还是生产手册,指挥者并不知道,要看传入的具体建造者是什么。但是不管建造者是谁,指挥官执行的就是一个流水线的操作,如开篇的图,简单分析下:
- constructSportsCar(建造跑车)建造流程:setCarType(确定汽车类型) ------> setSeats(建造汽车座位数) ------> setEngine(建造汽车引擎) ------> setTransmission(建造汽车变速箱) ------> setTripComputer(建造汽车行程仪) ------> setGPSNavigator(建造导航仪)
- constructCityCar(建造城市汽车)建造流程:setCarType(确定汽车类型) ------> setSeats(建造汽车座位数) ------> setEngine(建造汽车引擎) ------> setTransmission(建造汽车变速箱) ------> setTripComputer(建造汽车行程仪) ------> setGPSNavigator(建造导航仪)
- constructCityCar(建造SUV)建造流程:setCarType(确定汽车类型) ------> setSeats(建造汽车座位数) ------> setEngine(建造汽车引擎) ------> setTransmission(建造汽车变速箱) ------> setGPSNavigator(建造导航仪)
我们发现了构建SUV只有五个步骤,少了建造行程仪这一步骤(实际上很多SUV出厂的时候是不带行程仪的,需要自己额外加)。这个例子放在这里恰到好处,也就是说在建造的过程中,并不是所有的产品都需要完成抽奖建造者(Builder)中定义的所有步骤,可以按需实现,这是非常灵活的。
另一个好处就是对象的构建的过程是有序的,具体体现在下面的方法中,我们知道程序执行是从上往下执行的,这保证了程序创建出来的对象是没有问题的。不用建造者模式我们创建一个Car对象的流程是:1,先把那些组件先new出来 2,再调用Car的构造方法来创建对象。我们思考这样一个问题,如果第1个步骤中的对象我们少set了一个值,会出现什么情况?这样你的组件就是不完整的,一个不完整的组件去构建汽车,这个汽车自然也是有问题的。建造者模式通过在一个方法中用代码的执行顺序来保证建造过程的完整性。
public class Director {
public void constructSportsCar(Builder builder) {
builder.setCarType(CarType.SPORTS_CAR);
builder.setSeats(2);
builder.setEngine(new Engine(3.0, 0));
builder.setTransmission(Transmission.SEMI_AUTOMATIC);
builder.setTripComputer(new TripComputer());
builder.setGPSNavigator(new GPSNavigator());
}
public void constructCityCar(Builder builder) {
builder.setCarType(CarType.CITY_CAR);
builder.setSeats(2);
builder.setEngine(new Engine(1.2, 0));
builder.setTransmission(Transmission.AUTOMATIC);
builder.setTripComputer(new TripComputer());
builder.setGPSNavigator(new GPSNavigator());
}
public void constructSUV(Builder builder) {
builder.setCarType(CarType.SUV);
builder.setSeats(4);
builder.setEngine(new Engine(2.5, 0));
builder.setTransmission(Transmission.MANUAL);
builder.setGPSNavigator(new GPSNavigator());
}
}
总结
建造者模式实际开发并不常见,但是在JDK中有它的身影,比如StringBuffer与StringBuilder的append方法正是采用建造者模式 ,建造者模式高深莫测,实际上我们不需要指挥者也是可以实现建造者模式的,本文不展开。该模式将对象表示与构建过程分离,实现解耦,并且保证了复杂对象(对象的属性也是对象)创建的正确性。