需求
一个类,计算机类 Computer
包括如下的设备。
- cpu
- ram
- usbCount
- keyBoard
- display
而其他 3 个是可选参数,可选设备如下。
- usbCount
- keyBoard
- display
两种常用的方式
折叠构造器模式
JavaBean
缺点
第 1 种
- 第一种主要是使用及阅读不方便。当调用一个类的构造函数时
- 首先要决定使用哪一个,里面参数又很多,参数的类型也不一样
- 这样很容易搞混
第 2 种
- 在构建过程中对象的状态容易发生变化,造成错误
- 因为那个类中的属性是分步设置的,所以就容易出错
特点
- 在软件开发过程中有时需要创建一个复杂的对象,这个复杂对象通常由多个子部件按一定的步骤组合而成
- 产品都是由多个部件构成的,各个部件可以灵活选择,但其创建步骤都大同小异
- 这类产品的创建无法用前面介绍的工厂模式描述,只有建造者模式可以很好地描述该类产品的创建
建造者模式解决需求
- 第一步:创建目标类,也就是我们的产品
/**
* @author BNTang
*/
public class Computer {
/**
* 必须
*/
private final String cpu;
/**
* 必须
*/
private final String ram;
/**
* 可选
*/
private int usbCount;
/**
* 可选
*/
private String keyboard;
/**
* 可选
*/
private String display;
public Computer(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
public void setUsbCount(int usbCount) {
this.usbCount = usbCount;
}
public void setKeyboard(String keyboard) {
this.keyboard = keyboard;
}
public void setDisplay(String display) {
this.display = display;
}
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
", usbCount=" + usbCount +
", keyboard='" + keyboard + '\'' +
", display='" + display + '\'' +
'}';
}
}
- 第二步:创建抽象构建者类
/**
* @author BNTang
*/
public abstract class ComputerBuilder {
public abstract void buildCount(int usbCount);
public abstract void buildKeyboard(String keyBoard);
public abstract void buildDisplay(String display);
public abstract Computer makeComputer();
}
- 第三步:创建具体建造者类
/**
* @author BNTang
*/
public class HwComputerBuilder extends ComputerBuilder {
private final Computer computer;
public HwComputerBuilder(String cpu, String ram) {
computer = new Computer(cpu, ram);
}
public void buildCount(int usbCount) {
computer.setUsbCount(usbCount);
}
public void buildKeyboard(String keyBoard) {
computer.setKeyboard(keyBoard);
}
public void buildDisplay(String display) {
computer.setDisplay(display);
}
public Computer makeComputer() {
return computer;
}
}
- 第四步:创建指挥类
/**
* @author BNTang
*/
public class ComputerDirector {
private ComputerBuilder builder;
public void setBuilder(ComputerBuilder builder) {
this.builder = builder;
}
public Computer makeComputer(int useCount, String display, String keyBoard) {
builder.buildCount(useCount);
builder.buildDisplay(display);
builder.buildKeyboard(keyBoard);
return builder.makeComputer();
}
}
- 创建
Client
使用
/**
* @author BNTang
*/
public class Client {
public static void main(String[] args) {
ComputerDirector director = new ComputerDirector();
ComputerBuilder builder = new MacComputerBuilder("苹果处理器", "苹果RAM");
director.setBuilder(builder);
Computer computer = director.makeComputer(2, "苹果显示器", "苹果键盘");
System.out.println(computer);
}
}
定义
- 指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式
- 它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。即产品的组成部分是不变的,但每一部分是可以灵活选择的
建造者模式与工厂模式的区别
工厂模式
- 工厂模式注重于创建产品
- 工厂模式创建出来的产品都是一个样子
- 工厂模式的关注点是只要创建出对象就可以了
建造者模式
- 建造者模式注重于方法的调用顺序
- 建造者模式可以创建出复杂的产品,由各种复杂的部件组成
- 建造者模式不仅要创建产品,,还要知道该产品是由哪些部件组成的
建造者模式 4 个角色
- Product(产品角色)一个具体的产品对象
- Builder(抽象建造者)创建一个
Product
对象的各个部件指定的接口 / 抽象类 - ConcreteBuilder(具体建造者)实现接口,构建和装配各个部件
- Director(指挥者)构建一个使用 Builder 接口的对象,它主要是用于创建一个复杂的对象,它的作用,隔离了客户与对象的生产过程,负责控制产品对象的生产过程
UML 图
写法 2
- 在 Computer 中创建一个静态内部类
Builder
,然后将Computer
中的参数都复制到Builder
类中
/**
* @author BNTang
*/
public class Computer {
/**
* 必须
*/
private final String cpu;
/**
* 必须
*/
private final String ram;
/**
* 可选
*/
private final int usbCount;
/**
* 可选
*/
private final String keyboard;
/**
* 可选
*/
private final String display;
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
", usbCount=" + usbCount +
", keyboard='" + keyboard + '\'' +
", display='" + display + '\'' +
'}';
}
private Computer(Builder computerBuilder) {
this.cpu = computerBuilder.cpu;
this.ram = computerBuilder.ram;
this.usbCount = computerBuilder.usbCount;
this.keyboard = computerBuilder.keyboard;
this.display = computerBuilder.display;
}
public static class Builder {
/**
* 必须
*/
private final String cpu;
/**
* 必须
*/
private final String ram;
/**
* 可选
*/
private int usbCount;
/**
* 可选
*/
private String keyboard;
/**
* 可选
*/
private String display;
public Builder(String cup, String ram) {
this.cpu = cup;
this.ram = ram;
}
public Builder buildUsbCount(int usbCount) {
this.usbCount = usbCount;
return this;
}
public Builder buildKeyboard(String keyboard) {
this.keyboard = keyboard;
return this;
}
public Builder buildDisplay(String display) {
this.display = display;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
/**
* @author BNTang
*/
public class Client {
public static void main(String[] args) {
Computer computer = new Computer.Builder("因特尔", "三星")
.buildDisplay("三星24寸")
.buildKeyboard("罗技")
.buildUsbCount(2)
.build();
System.out.println(computer);
}
}
优缺点
优点
- 封装性好,构建和表示分离
- 扩展性好,各个具体的建造者相互独立,有利于系统的解耦
- 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险
缺点
- 产生多余的 Builder 对象
- 产品内部发生变化,建造者都要修改,成本较大
使用场景
- 如果一个对象有非常复杂的内部结构 (很多属性)
- 想把复杂对象的创建和使用分离
总结
- 用来创建复杂的复合对象
源码分析
StringBuilder
- JDK 的 StringBuilder 类中提供了 append() 方法,这就是一种链式创建对象的方法,开放构造步骤,最后调用
toString()
方法就可以获得一个完整的对象
SqlSessionFactoryBuilder
- MyBatis 中 SqlSessionFactoryBuiler 类用到了建造者模式
- 且在 MyBatis 中 SqlSessionFactory 是由 SqlSessionFactoryBuilder 产生的
- XMLConfigBuilder 负责 Configuration 各个组件的创建和装配,整个装配的流程化过程如下:
- XMLConfigBuilder 负责创建复杂对象 Configuration,其实就是一个具体建造者角色
- SqlSessionFactoryBuilder 只不过是做了一层封装去构建 SqlSessionFactory 实例,这就是建造者模式简化构建的过程