引子
曾经在博文中发表过一些关于“Builder形式”的错误言辞,误导了读者。今日在此虔诚地向Builder形式道个歉。
这个错误言辞出现在面试题 | 怎么写一个又好又快的日志库?(一)。在该篇博文中,我如是说:
Kotlin 相较于 Java 的最大优势就是下降复杂度。在库接口设计及内部实现时就要充分发挥 Kotlin 的优势,比如 Kotlin 的国际里已不需要 Builder 形式了。
Builder 形式有如下优势:
- 为参数标示语义:在Builder形式中,每个特点的赋值都是一个函数,函数名标示了特点语义。
- 可选参数&分批赋值:Builder形式中,除了必选参数,其他参数是可选的,可分批赋值。而直接运用结构函数有必要一会儿为一切参数赋值。
- 添加参数约束条件:能够在参数不符合要求时,抛出反常。
但 Builder 形式也有价值,新增了一个中间类
Builder
。运用 Kotlin 的
命名参数
+参数默认值
+require()
语法,在没有任何副作用的情况下就能实现 Builder 形式:class Person( val name: String, //'为以下可选参数设置默认值' val gender: Int = 1, val age: Int= 0, val height: Int = 0, val weight: Int = 0 ) //'运用命名参数构建 Person 实例' val p = Person(name = “taylor”,gender = 1,weight = 43)
命名参数为每个实参赋予了语义,而且不需要按结构方法中声明的次序来赋值,能够跳着来。
假如想添加参数约束条件能够调用
require()
方法:data class Person( // 这个是必选参数 val name: String, val gender: Int = 1, val age: Int= 0, val height: Int = 0, val weight: Int = 0 ){ //'在结构函数被调用的时分履行参数合法查看' init { require(name.isNotEmpty()){”name cant be empty“} } }
此时假如像下面这样结构 Person,则会抛出反常:
val p = Person(name="",gender = 1) java.lang.IllegalArgumentException: name cant be empty
本来在 build() 方法中履行的额定初始化逻辑也能够悉数写在
init
代码块中。
上述结论好像在给定的场景中找不出任何缺点。
但我遗漏了一个场景,“构建目标”和“特点赋值”是能够分开进行的。比如下面这个场景。
syntax = "proto3";
message Event {
int64 time = 1; // 事情生成时刻
string id = 2; // 事情唯一标识符
string name = 3;// 事情称号
}
message BatchEvent {
repeated Event event = 1;
}
运用 protobuf 界说了两个结构体,其中Event
表明单个事情,而BatchEvent
表明批量事情,它持有多个事情。
protobuf对应的java代码会运用建造者形式,这为构建批量事情提供了便利。
假设有一个事情流:
val initEvent = event {
time = System.currentTimeMillis()
id = UUID.randomUUID()
name = "init"
}
val eventFlow = MutableStateFlow(initEvent)
不同类型的事情在 eventFlow 中流动。当遇到 flush 事情时,会将之前堆积的一切事情包装成 BatchEvent 上传网络。
这就是一个特点赋值和构建目标分开进行的场景,即一直为 BatchEvent 目标的 events 特点追加事情,直到遇到了特别事情时才构建目标:
var builder = BatchEvent.newBuilder()
eventFlow.collect { event ->
builder.addEvent(event)
if(event.name == "flush") {
upload(builder.build())
builder = BatchEvent.newBuilder()
}
}
最终再下一个对 Builder 形式完整的总结:
- 为参数标示语义:在Builder形式中,每个特点的赋值都是一个函数,函数名标示了特点语义。
- 可选参数&分批赋值:Builder形式中,除了必选参数,其他参数是可选的,可分批赋值。而直接运用结构函数有必要一会儿为一切参数赋值。
- 添加参数约束条件:能够在参数不符合要求时,抛出反常。
- 便利实现特点赋值和结构目标的别离。