1. 模拟需求背景
假设现在咱们要构造一个电脑的实体类,他的属性呢,有2个必选,3个可选
如下:
/**
* 电脑实体类
*
* @author wql
* @date 2021/10/11 11:50
*/
public class Computer {
private String cpu; //必须
private String ram; //必须
private Integer usbCount; //可选
private String keyboard; //可选
private String display; //可选
}
2. 未使用建造者模式之前
咱们是这样干的:
2.1. 方案1: 折叠构造器
/**
* 电脑实体类
*
* @author wql
* @date 2021/10/11 11:50
*/
public class Computer {
private String cpu; //必须
private String ram; //必须
private Integer usbCount; //可选
private String keyboard; //可选
private String display; //可选
public Computer(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
public Computer(String cpu, String ram, Integer usbCount) {
this.cpu = cpu;
this.ram = ram;
this.usbCount = usbCount;
}
public Computer(String cpu, String ram, Integer usbCount, String keyboard) {
this.cpu = cpu;
this.ram = ram;
this.usbCount = usbCount;
this.keyboard = keyboard;
}
public Computer(String cpu, String ram, Integer usbCount, String keyboard, String display) {
this.cpu = cpu;
this.ram = ram;
this.usbCount = usbCount;
this.keyboard = keyboard;
this.display = display;
}
}
按需使用响应的构造器构建需要的类,
但使用及阅读不方便。你可以想象一下,当你要调用一个类的构造函数时,你首先要决定使用哪一个,然后里面又是一堆参数,如果这些参数的类型很多又都一样,你还要搞清楚这些参数的含义,很容易就传混了。。。
2.2. 方案2: get set方法赋值
/**
* 电脑实体类
*
* @author wql
* @date 2021/10/11 11:50
*/
public class Computer {
private String cpu; //必须
private String ram; //必须
private Integer 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 Integer getUsbCount() {
return usbCount;
}
public void setUsbCount(Integer usbCount) {
this.usbCount = usbCount;
}
public String getKeyboard() {
return keyboard;
}
public void setKeyboard(String keyboard) {
this.keyboard = keyboard;
}
public String getDisplay() {
return display;
}
public void setDisplay(String display) {
this.display = display;
}
}
这种方式呢,在构建过程中对象的状态容易发生变化,造成错误。因为那个类中的属性是分步设置的,所以就容易出错。
3. 使用建造者模式之后
咱们是这样干的:
public class Computer {
private final String cpu;//必须
private final String ram;//必须
private final int usbCount;//可选
private final String keyboard;//可选
private final 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);
}
}
//省略getter方法
}
如何使用:
Computer computer=new Computer.Builder("因特尔","三星")
.setDisplay("三星24寸")
.setKeyboard("罗技")
.setUsbCount(2)
.build();
3.1 引入spring的上下文
在上面,我们是使用构建者模式的时候可以使用spring的上下文,而不需要我们自己去new
package com.greatmicro.foundation.common.core.context;
import org.apache.commons.lang3.Validate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* Spring上下文持有器
*/
@Component
public class SpringContextHolder implements ApplicationContextAware {
public SpringContextHolder() {
}
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
public static Object getBean(String beanName) {
assertContextInjected();
return applicationContext.getBean(beanName);
}
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
public static <T> T getBean(String beanName, Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(beanName, requiredType);
}
public static String getProperty(String key) {
return applicationContext.getEnvironment().getProperty(key);
}
private static void assertContextInjected() {
Validate.validState(applicationContext != null, "ApplicationContext属性未注入, 请在Application中定义SpringContextHolder.", new Object[0]);
}
public static void clearHolder() {
applicationContext = null;
}
}
从而上面的如何使用就可以改成如下:
SpringContextHolder.getBean(ReceiveBuilder.class).
.setCpu("")
.setRam("")
.setDisplay("三星24寸")
.setKeyboard("罗技")
.setUsbCount(2)
.build();
3.2 总结:
当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。
注: 学习建造者最好的办法,就是看下lombok的Builder注解是怎么工作的就行了