建造者模式使用多个简单的对象一步一步构建成一个复杂的对象。
当一个类的构造函数参数个数超过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;//可选,默认为0private 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;//可选,默认为0private 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;//可选,默认为0private 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;}@Overridepublic 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);}@Overridepublic void setUsbCount() {computer.setUsbCount(4);}@Overridepublic void setKeyboard() {computer.setKeyboard("联想键盘");}@Overridepublic void setDisplay() {computer.setDisplay("联想显示器");}@Overridepublic Computer getComputer() {return computer;}
}
public class MacComputerBuilder extends ComputerBuilder{private Computer computer;public MacComputerBuilder(String cpu, String ram) {computer = new Computer(cpu, ram);}@Overridepublic void setUsbCount() {computer.setUsbCount(2);}@Overridepublic void setKeyboard() {computer.setKeyboard("苹果键盘");}@Overridepublic void setDisplay() {computer.setDisplay("苹果显示器");}@Overridepublic 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();//1ComputerBuilder macBuilder = new MacComputerBuilder("I5处理器","三星125");//2director.makeComputer(macBuilder);//3Computer macComputer=macBuilder.getComputer();//4System.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 写到了要构建的产品类里面,最后采用了链式调用。
秒懂设计模式之建造者模式(Builder pattern) - 知乎 (zhihu.com)