一、概念
当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。假设要创建一个 Computer 对象,cpu、ram是必选,display、keyboard是可选。
| 优点 | 建造者独立,易扩展。 |
| 便于控制细节风险。 | |
| 缺点 | 产品需有共同点,适用范围有限。 |
| 若内部变化复杂,会产生大量建造类。 |
1.1 与工厂模式区别
工厂主要变更的地方是产品的变更,而Builder模式主要变更的地方是director(组装逻辑)。工厂更倾向基本组件的生产,而Builder是这些基本组件的组装。
1.2 Android 使用场景
| Dialog创建 | 如自定义对话框,通过Builder类分步设置标题、内容、按钮、自定义视图等,避免多参数构造函数的混乱。 |
| Notification创建 | 系统NotificationCompat.Builder就是典型应用,通过setContentTitle()、setSmallIcon()等方法配置通知,最终build()生成实例。 |
| 网络请求参数组装 | Retrofit或OkHttp中,通过建造者模式设置url、headers、params、method等,构建完整的请求对象。 |
二、Java写法
2.1 构造函数重载(不推荐)
阅读起来不方便,参数太多容易混淆。
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;
}
}
2.2 JavaBean
构建过程中对象状态容易发生变化,分步设置属性容易出错。
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;}
}
2.3 构建者模式
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写法
由于 Kotlin 支持默认参数(形参有默认值)、命名参数(乱序指定形参名赋值)、作用域函数(apply),大部分情况下并不需要构建者模式,除了当我们希望一个对象经过N步才能完成构建且对象不可变时。假设 Computer 需要配置 cpu、ram 才是一个完整的对象,但它俩不能同时到位,如果先创建对象再后期 setter 进去,Computer就是可变对象了。
//目标类添加private主构造,属性只读供外部使用。
class Computer private constructor(
val cpu: String,
val ram: String,
val display: String,
val keyboard: String,
) {
//目标类中创建内部类Builder(Kotlin的内部类是static的)。
//必传参数放在主构造,可选参数放在属性
class Builder(val cpu: String, val ram: String) {
//私有化属性并提供自定义的方法对属性赋值(为了可以链式调用)。
private var display: String = "LG"
private var keyboard: String = "罗技"
fun setDisplay(str: String) = apply {display = str}
fun setKeyboard(str: String) = apply {keyboard = str}
//创建build()方法,创建目标类实例并返回
fun build() = Computer(
cpu = cpu,
ram = ram,
display = display,
keyboard = keyboard
)
}
}
//使用
val computer = Computer.Builder("因特尔","三星")
.setDisplay("LG")
.setKeyboard("罗技")
.build()
文章介绍了在Java和Kotlin中如何使用构造者模式来处理构造函数参数过多和可选参数的问题。通过对比构造函数重载、JavaBean方式,强调了构建者模式在创建对象时的清晰性和灵活性。在Kotlin中,由于默认参数和命名参数,通常情况下不需要构建者模式,但在特定场景下,如对象需要多步构建且保持不可变性时,依然适用。
920

被折叠的 条评论
为什么被折叠?



