建造者模式使用多个简单的对象一步一步构建成一个复杂的对象。
当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。
场景
当一个类的构造函数参数超过4个,而且这些参数有些是可选的时,我们通常有两种办法来构建它的对象。 例如我们现在有如下一个类计算机类Computer,其中cpu与ram是必填参数,而其他3个是可选参数,那么我们如何构造这个类的实例呢,通常有两种常用的方式:
public class Computer {
private String cpu;//必须
private String ram;//必须
private int usbCount;//可选
private String keyboard;//可选
private String display;//可选
}
① 折叠构造函数模式
public class Computer {
private String cpu;//必须
private String ram;//必须
private int usbCount;//可选,默认为0
private String keyboard;//可选,默认为罗技键盘
private String display;//可选,默认为三星显示器
public Computer(String cpu, String ram) {
this(cpu, ram, 0);
}
public Computer(String cpu, String ram, int usbCount) {
this(cpu, ram, usbCount, "罗技键盘");
}
public Computer(String cpu, String ram, int usbCount, String keyboard) {
this(cpu, ram, usbCount, keyboard, "三星显示器");
}
public Computer(String cpu, String ram, int usbCount, String keyboard, String display) {
this.cpu = cpu;
this.ram = ram;
this.usbCount = usbCount;
this.keyboard = keyboard;
this.display = display;
}
}
② Javabean模式
public class Computer {
private String cpu;//必须
private String ram;//必须
private int usbCount;//可选
private String keyboard;//可选
private String display;//可选
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getRam() {
return ram;
}
public void setRam(String ram) {
this.ram = ram;
}
public int getUsbCount() {
return usbCount;
}
...
}
这也可用lombok依赖包的@Data注解替代~
两种方式的弊端
第一种使用和阅读不方便,当使用该类的构造函数时,需要搞清楚各参数的含义,而且还是按照严格的顺序。
第二种由于类的属性是分布设置的,那么构建过程中对象的状态容易发生变化,容易出错。
那么builder模式可以较好地解决以上问题!
步骤
在Computer 中创建一个静态内部类 Builder,然后将Computer 中的参数都复制到Builder类中。
在Computer中创建一个private的构造函数,参数为Builder类型
在Builder中创建一个public的构造函数,参数为Computer中必填的那些参数,cpu 和ram。
在Builder中创建设置函数,对Computer中那些可选参数进行赋值,返回值为Builder类型的实例
在Builder中创建一个build()方法,在其中构建Computer的实例并返回
也可直接使用lombok依赖包的@Builder注解一键解决。
具体代码
public class Computer {
private String cpu;//必须
private String ram;//必须
private int usbCount;//可选,默认为0
private String keyboard;//可选,默认为罗技键盘
private String display;//可选,默认为三星显示器
private Computer(Builder builder){
this.cpu=builder.cpu;
this.ram=builder.ram;
this.usbCount=builder.usbCount;
this.keyboard=builder.keyboard;
this.display=builder.display;
}
public static class Builder{
private String cpu;//必须
private 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 setUsbCount(int usbCount) {
this.usbCount = usbCount;
return this;
}
public Builder setKeyboard(String keyboard) {
this.keyboard = keyboard;
return this;
}
public Builder setDisplay(String display) {
this.display = display;
return this;
}
public Computer build(){
return new Computer(this);
}
}
}
使用
Computer computer = new Computer.Builder("AMD","小米")
.setUsbCount(0)
.setKeyboard("罗技")
.setDisplay("三星24寸").build();
@Builder注解一键解决
@Builder
public class Computer {
private String cpu;//可选
private String ram;//可选
private int usbCount;//可选
private String keyboard;//可选
private String display;//可选
}
class test{
public static void main(String[] args) {
Computer computer = Computer.builder().cpu("AMD").ram("金士顿")
.usbCount(0)
.keyboard("罗技")
.display("三星24寸")
.build();
}
}
使用方式稍微不一样~
上面的内容是Builder在Java中一种简化的使用方式,经典的Builder 模式与其有一定的不同,如UML图:
各角色
Product: 最终要生成的对象,例如 Computer实例。
Builder: 构建者的抽象基类(有时会使用接口代替)。其定义了构建Product的抽象步骤,其实体类需要实现这些步骤。其会包含一个用来返回最终产品的方法Product getProduct(),例如 ComputerBuilder抽象类。
ConcreteBuilder: Builder的实现类,如LenovoComputerBuilder类和MacComputerBuilder类。
Director: 决定如何构建最终产品的算法, 其会包含一个负责组装的方法void Construct(Builder builder), 在这个方法中通过调用builder的方法,就可以设置builder,例如 ComputerDirector类。
等设置完成后,就可以通过builder的 getProduct() 方法获得最终的产品。
1.目标Computer类
public class Computer {
private String cpu;//必须
private String ram;//必须
private int usbCount;//可选,默认为0
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;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
", usbCount=" + usbCount +
", keyboard='" + keyboard + '\'' +
", display='" + display + '\'' +
'}';
}
}
2.抽象构建者类
public abstract class ComputerBuilder {
public abstract void setUsbCount();
public abstract void setKeyboard();
public abstract void setDisplay();
public abstract Computer getComputer();
}
3.实体构建者类
public class LenovoComputerBuilder extends ComputerBuilder{
private Computer computer;
public LenovoComputerBuilder(String cpu, String ram) {
computer=new Computer(cpu,ram);
}
@Override
public void setUsbCount() {
computer.setUsbCount(4);
}
@Override
public void setKeyboard() {
computer.setKeyboard("联想键盘");
}
@Override
public void setDisplay() {
computer.setDisplay("联想显示器");
}
@Override
public Computer getComputer() {
return computer;
}
}
public class MacComputerBuilder extends ComputerBuilder{
private Computer computer;
public MacComputerBuilder(String cpu, String ram) {
computer = new Computer(cpu, ram);
}
@Override
public void setUsbCount() {
computer.setUsbCount(2);
}
@Override
public void setKeyboard() {
computer.setKeyboard("苹果键盘");
}
@Override
public void setDisplay() {
computer.setDisplay("苹果显示器");
}
@Override
public Computer getComputer() {
return computer;
}
}
4.指导者类(Director)
public class ComputerDirector {
public void makeComputer(ComputerBuilder builder){
builder.setUsbCount();
builder.setDisplay();
builder.setKeyboard();
}
}
5.使用
首先生成一个director,然后生成一个目标builder ,接着使用director组装builder,组装完毕后使用builder创建产品实例。
ComputerDirector director = new ComputerDirector();//1
ComputerBuilder macBuilder = new MacComputerBuilder("I5处理器","三星125");//2
director.makeComputer(macBuilder);//3
Computer macComputer=macBuilder.getComputer();//4
System.out.println("mac computer:"+macComputer.toString());
ComputerBuilder lenovoBuilder=new LenovoComputerBuilder("I7处理器","海力士222");
director.makeComputer(lenovoBuilder);
Computer lenovoComputer=lenovoBuilder.getComputer();
System.out.println("lenovo computer:"+lenovoComputer.toString());
总结
其实之前用的是的变种, 首先其省略了director 这个角色,将构建算法交给了client端,其次将builder 写到了要构建的产品类里面,最后采用了链式调用。