建造者模式与其他创建型模式一样服务于相同的目标,只不过它出于不同的原因,通过不同的方式实现。在开发复杂的应用程序时,代码往往会变得非常复杂。类会封装更多的功能,类的结构也会变得更加复杂。随着功能量的增加,就需要涵盖更多场景,从而需要构建更多不同的类。
当需要实例化一个复杂的类,以得到不同结构和不同内部状态的对象时,我们可以使用不同的类对它们的实例化操作逻辑分别进行封装,这些类就被称为建造者。每当需要来自同一个类但具有不同结构的对象时,就可以通过构造另一个建造者来进行实例化。
建造者模式中包含以下类:
- Product(产品类):需要为其构建对象的类,是具有不同表现形式的复杂或复合对象。
- Builder(抽象建造者类):用于声明构建产品类的组成部分的抽象类或接口。它的作用是仅公开构建产品类的功能,隐藏产品类的其他功能;将产品类与构建产品类的更高级的类分离开。
-
ConcreteBuilder(具体建造者类:用于实现抽象建造者类接口中声明的方法。除此之外,它还通过
getResult
方法返回构建好的产品类。 - Director(导演类):用于指导如何构建对象的类。在建造者模式的某些变体中,导演类已被移除,其角色被客户端或抽象建造者类所代替。
汽车建造者样例
存在一个Car
类,需要为它创建实例。通过向汽车中添加不同的组件,我们分别可以制造燃油车、纯电车、混动车等。当开始设计软件时,需要认识到以下几点:
-
Car
类非常复杂,创建类的对象也是一个复杂的操作。在Car
类的构造函数中添加所有的实例化逻辑将使其变得体量庞大。 - 我们需要构建多种类型的汽车类。针对这种情况,我们通常会添加多个不同的构造函数,但直觉告诉我们这并非最好的解决方案。
- 将来我们可能需要构建多种不同类型的汽车对象。由于市场上对于混动车的需求非常高涨,在不久的将来,我们应该做好准备进行代码扩展而不是重新修改代码。
AbstractCarBuilder
是建造者基类,我们创建了两个具体建造者类:ElectricCarBuilder
和GasolineCarBuilder
。每个建造者实现类都分别实现了AbstractCarBuilder
的所有抽象方法。那些类中不需要的方法(例如ElectricCarBuilder
中的addGasTank
方法)会被置空或抛出异常。ElectricCar类和GasolineCar类内部结构是不同的。
/**
* @author: Jay Mitter
* @date: 2020-08-15 10:37
* @description: 抽象构建者
*/
public abstract class AbstractCarBuilder {
/**
* 构建一个无参的BCar
* @return
*/
public abstract void buildCar();
/**
* 设置发动机类型
* @param engineType
*/
public abstract void setEngineType(String engineType);
/**
* 设置轮胎类型
* @param wheelType
*/
public abstract void setWheelType(String wheelType);
/**
* 设置颜色
* @param color
*/
public abstract void setColor(String color);
/**
* 设置传动方式,手动、自动
* @param transmission
*/
public abstract void setTransmission(String transmission);
/**
* 汽油车加汽油
* @param capacity
*/
public abstract void addGasTank(String capacity);
/**
* 新能源车加电池
* @param capacity
*/
public abstract void addBatteries(String capacity);
/**
* 返回构建的BCar
* @return BCar
*/
public abstract BCar getCar();
}
/**
* @author: Jay Mitter
* @date: 2020-08-15 10:37
* @description: 燃油车
*/
public class GasolineCarBuilder extends AbstractCarBuilder {
private BCar bCar;
@Override
public void buildCar() {
this.bCar = new BCar();
}
@Override
public void setEngineType(String engineType) {
this.bCar.setEngineType(engineType);
}
@Override
public void setWheelType(String wheelType) {
this.bCar.setWheelType(wheelType);
}
@Override
public void setColor(String color) {
this.bCar.setColor(color);
}
@Override
public void setTransmission(String transmission) {
this.bCar.setTransmission(transmission);
}
@Override
public void addGasTank(String capacity) {
this.bCar.setGasTank(capacity);
}
@Override
public void addBatteries(String capacity) {
throw new RuntimeException("燃油车不能加电池!");
}
@Override
public BCar getCar() {
return this.bCar;
}
}
/**
* @author: Jay Mitter
* @date: 2020-08-15 10:37
* @description: 纯电车
*/
public class ElectricCarBuilder extends AbstractCarBuilder {
private BCar bCar;
@Override
public void buildCar() {
this.bCar = new BCar();
}
@Override
public void setEngineType(String engineType) {
this.bCar.setEngineType(engineType);
}
@Override
public void setWheelType(String wheelType) {
this.bCar.setWheelType(wheelType);
}
@Override
public void setColor(String color) {
this.bCar.setColor(color);
}
@Override
public void setTransmission(String transmission) {
this.bCar.setTransmission(transmission);
}
@Override
public void addGasTank(String capacity) {
throw new RuntimeException("纯电车不能加油!");
}
@Override
public void addBatteries(String capacity) {
this.bCar.setBatteries(capacity);
}
@Override
public BCar getCar() {
return this.bCar;
}
}
导演类使用抽象建造者类来创建新的汽车对象。buildElectricCar
和buildGasolineCar
两个方法略有不同:
/**
* @author: Jay Mitter
* @date: 2020-08-15 10:37
* @description:
*/
public class CarBuilderDirector {
public BCar buildElectricCar(AbstractCarBuilder carBuilder) {
carBuilder.buildCar();
carBuilder.setEngineType("Nio");
carBuilder.setWheelType("Michelin");
carBuilder.setColor("Green");
carBuilder.setTransmission("Auto");
// 会抛异常:纯电车不能加油
// carBuilder.addGasTank("Nio gasTank");
carBuilder.addBatteries("Nio batteries");
return carBuilder.getCar();
}
public BCar buildGasolineCar(AbstractCarBuilder carBuilder) {
carBuilder.buildCar();
carBuilder.setEngineType("BYD");
carBuilder.setWheelType("Good Year");
carBuilder.setColor("Gray");
carBuilder.setTransmission("Manual");
carBuilder.addGasTank("BYD gasTank");
// 会抛异常:燃油车不能加电池
// carBuilder.addBatteries("BYD batteries");
return carBuilder.getCar();
}
}
如果想要制造一辆既有电动又有汽油发动机的混合动力汽车,扩展一个HybridCarBuilder
:
/**
* @author: Jay Mitter
* @date: 2020-08-15 10:37
* @description: 混动车
*/
public class HybridCarBuilder extends AbstractCarBuilder {
private BCar bCar;
@Override
public void buildCar() {
this.bCar = new BCar();
}
@Override
public void setEngineType(String engineType) {
this.bCar.setEngineType(engineType);
}
@Override
public void setWheelType(String wheelType) {
this.bCar.setWheelType(wheelType);
}
@Override
public void setColor(String color) {
this.bCar.setColor(color);
}
@Override
public void setTransmission(String transmission) {
this.bCar.setTransmission(transmission);
}
@Override
public void addGasTank(String capacity) {
// 混动车既可以加油也可以加电池
this.bCar.setGasTank(capacity);
}
@Override
public void addBatteries(String capacity) {
// 混动车既可以加油也可以加电池
this.bCar.setBatteries(capacity);
}
@Override
public BCar getCar() {
return this.bCar;
}
}
导演类添加buildHybridCar
方法:
public BCar buildHybridCar(AbstractCarBuilder carBuilder) {
carBuilder.buildCar();
carBuilder.setEngineType("TOYOTA");
carBuilder.setWheelType("Continental AG");
carBuilder.setColor("White");
carBuilder.setTransmission("Auto");
carBuilder.addGasTank("Toyota gasTank");
carBuilder.addBatteries("Tesla batteries");
return carBuilder.getCar();
}
测试:
/**
* 创建型模式——建造者模式1
*/
@Test
public void testBuilder1() {
CarBuilderDirector carBuilderDirector = new CarBuilderDirector();
ElectricCarBuilder electricCarBuilder = new ElectricCarBuilder();
BCar electricCar = carBuilderDirector.buildElectricCar(electricCarBuilder);
System.out.println(JSON.toJSONString(electricCar));
GasolineCarBuilder gasolineCarBuilder = new GasolineCarBuilder();
BCar gasolineCar = carBuilderDirector.buildGasolineCar(gasolineCarBuilder);
System.out.println(JSON.toJSONString(gasolineCar));
HybridCarBuilder hybridCarBuilder = new HybridCarBuilder();
BCar hybridCar = carBuilderDirector.buildHybridCar(hybridCarBuilder);
System.out.println(JSON.toJSONString(hybridCar));
}
拥有方法链的匿名建造者
构建来自相同类但具有不同形式的对象的最直接方法就是构建多个构造函数,按照不同的场景进行不同的实例化操作。使用建造者模式避免这种情况是个不错的实践,在《EffectiveJava》一书中,Joshua Bloch建议使用内部建造者类和方法链来代替构建多个构造函数。
方法链是指通过特定方法返回当前对象(this)的一种技术。通过这种技术,可以以链的形式调用方法。
package com.mitter.book.review.designpattern.builder;
import lombok.Data;
/**
* @author: Jay Mitter
* @date: 2020-08-15 10:39
* @description:
*/
@Data
public class CCar {
/**
* 发动机类型,共享
*/
private String engineType;
/**
* 轮胎类型,共享
*/
private String wheelType;
/**
* 颜色,共享
*/
private String color;
/**
* 变速箱,共享
*/
private String transmission;
/**
* 油箱,燃油车
*/
private String gasTank;
/**
* 电池,纯电车
*/
private String batteries;
/**
* 内部类方法链的匿名建造者
*/
public static class Builder {
private CCar cCar = new CCar();
public Builder setEngineType(String engineType) {
cCar.setEngineType(engineType);
return this;
}
public Builder setWheelType(String wheelType) {
cCar.setWheelType(wheelType);
return this;
}
public Builder setColor(String color) {
cCar.setColor(color);
return this;
}
public Builder setTransmission(String transmission) {
cCar.setTransmission(transmission);
return this;
}
public Builder setGasTank(String gasTank) {
cCar.setGasTank(gasTank);
return this;
}
public Builder setBatteries(String batteries) {
cCar.setBatteries(batteries);
return this;
}
public CCar build() {
return this.cCar;
}
}
}
测试:
/**
* 创建型模式——建造者模式3
*/
@Test
public void testBuilder3() {
CCar cCar = new CCar.Builder().setEngineType("BMW")
.setWheelType("Michelin")
.setColor("Red")
.setTransmission("Auto")
.setGasTank("60L")
.setBatteries("300 kwh")
.build();
System.out.println(cCar);
}
使用Lombok
的@Builder
注解实现链式构建者模式:
/**
* @author: Jay Mitter
* @date: 2020-08-15 10:39
* @description:
*/
@Data
@Builder
public class DCar {
/**
* 发动机类型,共享
*/
private String engineType;
/**
* 轮胎类型,共享
*/
private String wheelType;
/**
* 颜色,共享
*/
private String color;
/**
* 变速箱,共享
*/
private String transmission;
/**
* 油箱,燃油车
*/
private String gasTank;
/**
* 电池,纯电车
*/
private String batteries;
}
测试:
/**
* 创建型模式——建造者模式4
*/
@Test
public void testBuilder4() {
DCar dCar = DCar.builder().engineType("Benz")
.wheelType("Good Year")
.color("Black")
.transmission("Manual")
.gasTank("58L")
.batteries("350 KWH")
.build();
System.out.println(dCar);
}
网友评论