当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。假设要创建一个 Computer 对象,cpu、ram是必选,display、keyboard是可选。
阅读起来不方便,参数太多容易混淆。
class Computer {
private String cpu; //必选
private String ram; //必选
private String display; //可选
private String keyboard; //可选
public Computer(String cpu, String ram) {
this(cpu, ram, "LG显示器");
}
public Computer(String cpu, String ram, String display) {
this(cpu, ram, display, "罗技鼠标");
}
public Computer(String cpu, String ram, String display, String keyboard) {
this.cpu = cpu;
this.ram = ram;
this.display = display;
this.keyboard = keyboard;
}
}
构建过程中对象状态容易发生变化,分步设置属性容易出错。
class Computer {
private String cpu; //必选
private String ram; //必选
private String display = "LG"; //可选
private String keyboard = "罗技"; //可选
public Computer(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
//setter和getter
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 String getDisplay() {return display;}
public void setDisplay(String display) {this.display = display;}
public String getKeyboard() {return keyboard;}
public void setKeyboard(String keyboard) {this.keyboard = keyboard;}
}
class Computer {
private final String cpu; //必选
private final String ram; //必选
private final String display; //可选
private final String keyboard; //可选
//目标类中创建private的构造函数,形参类型为Builder。
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.display = builder.display;
this.keyboard = builder.keyboard;
}
//目标类中创建静态内部类Builder,将参数都复制到Builder中。
public static class Builder {
private String cpu;
private String ram;
private String display;
private String keyboard;
//Builder中创建public的构造函数,参数为目标类中必填的参数(此处为cpu、ram)。
public Builder(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
//Builder中创建设置函数,对可选参数进行赋值(此处为display、keyboard),返回Builder实例。
public Builder setDisplay(String display) {
this.display = display;
return this;
}
public Builder setKeyboard(String keyboard) {
this.keyboard = keyboard;
return this;
}
public String getDisplay() { return display; }
public String getKeyboard() { return keyboard; }
//Builder中创建build()方法,返回目标类的实例。
public Computer build() {
return new Computer(this);
}
}
}
//使用
Computer computer = new Computer.Builder("因特尔", "三星")
.setDisplay("LG")
.setKeyboard("罗技")
.build();
由于 Kotlin 支持默认参数(形参有默认值)和命名参数(指定形参名赋值),大部分情况下并不需要构建者模式,除了当我们希望一个对象经过N步才能完成构建且对象不可变时。假设 Computer 需要配置 cpu、ram 才是一个完整的对象,但它俩不能同时到位,如果先创建对象再后期 setter 进去,Computer就是可变对象了。
//目标类添加peivate主构造,属性只读供外部使用。
class Computer private constructor(
val cpu: String,
val ram: String,
val display: String,
val keyboard: String,
) {
//目标类中添加private次构造,形参为Builder用来给目标类的属性赋值。
private constructor(builder: Builder) : this(
builder.cpu,
builder.ram,
builder.display,
builder.keyboard
)
//目标类中创建内部类Builder(kt的内部类是static的)。
class Builder(val cpu: String, val ram: String) {
//私有化setter提供自定义的方法对属性赋值(为了通过点调用让IDE提示可以配置哪些)。
var display: String = "LG"
private set
var keyboard: String = "罗技"
private set
fun setDisplay(str: String) = apply {display = str}
fun setKeyboard(str: String) = apply {keyboard = str}
//Builder中创建build()方法,返回目标类实例。
fun build() = Computer(this)
}
}
//使用
val computer = Computer.Builder("因特尔","三星")
.setDisplay("LG")
.setKeyboard("罗技")
.build()
因为不方便用小数点来让IDE显示可以配置哪些参数,还会和普通函数混在一起显示。
class Computer private constructor(
val cpu: String,
val ram: String,
val display: String,
val keyboard: String,
) {
private constructor(builder: Builder) : this(
builder.cpu,
builder.ram,
builder.display,
builder.keyboard
)
//伴生对象离来完成对Builder的配置
companion object {
inline fun build(block: Builder.() -> Unit) = Builder().apply(block).build()
}
class Builder(val cpu: String, val ram: String) {
var display: String = "LG"
var keyboard: String = "罗技"
fun build() = Computer(this)
}
}
val computer = Computer.builder {
display = "LG"
keyboard = "罗技"
}