为何建造者模式的实现不应设计为不可变?
建造者模式:可变实现是否更必要?
先看两个Kotlin实现的StringBuilder:
可变版本
class StringBuilder { private val items = mutableListOf<String>() fun append(item: String): StringBuilder { items.add(item) return this } override fun toString(): String { return items.joinToString("") } }
这个版本内部依赖可变列表items累积内容,每次调用append都会修改自身状态并返回当前实例。
不可变版本
class StringBuilder(private val items: List<String> = emptyList()) { fun append(item: String): StringBuilder { return StringBuilder(items + item) } override fun toString(): String { return items.joinToString("") } }
这个完全不可变的实现,每次调用append都会创建包含新内容的StringBuilder实例,原实例状态始终保持不变。
维基百科中建造者模式词条的“缺点”部分提到:
建造者类必须是可变的。
但显然这个说法并不准确——上面的例子就证明了不可变建造者是可行的。而将“必须可变”列为缺点,也侧面体现了不可变设计的优势。
那有没有特定场景或原因,使得建造者模式应当采用可变设计?
我能想到的最核心原因是性能与GC开销:不可变建造者每次调用方法都会生成新实例,在频繁构建复杂对象的场景下,大量临时对象的创建和销毁会显著增加垃圾回收的压力,拖慢程序运行速度。
除此之外,还有几个场景更适合可变建造者:
- 状态复用与共享:如果需要在多个逻辑分支中复用建造者的中间状态,可变建造者可以直接修改自身,无需创建多个实例;不可变建造者则需要传递不同的实例,会增加代码复杂度和内存占用。
- 构建过程的调试与跟踪:可变建造者的状态是连续变化的,调试时可以直观跟踪每一步的修改;不可变建造者每次都是全新实例,跟踪状态变化会更繁琐。
- 现有生态的兼容性:很多主流类库(比如Java标准库的
StringBuilder)的建造者都是可变设计,采用可变实现能更好地兼容现有代码,减少适配成本。
内容的提问来源于stack exchange,提问作者Matthew Layton
相关产品推荐
相关产品推荐

