一、基本概念
定义
建造者模式将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
核心思想
- 分离构造逻辑:将对象的构造过程分解为多个步骤
- 链式调用:提供流畅的API接口
- 参数验证:在最终构建时进行参数验证
- 不可变对象:通常构建不可变对象
二、基本结构
四个核心角色
// 1. Product (产品) - 要构建的复杂对象
data class Car(
val brand: String,
val model: String,
val color: String,
val engineSize: Double,
val hasSunroof: Boolean,
val hasGPS: Boolean
) {
// 可能包含复杂的构造逻辑
init {
require(brand.isNotBlank()) { "品牌不能为空" }
require(engineSize > 0) { "发动机排量必须大于0" }
}
}
// 2. Builder (建造者接口/抽象类)
abstract class CarBuilder {
protected var brand: String = ""
protected var model: String = ""
protected var color: String = "白色"
protected var engineSize: Double = 1.6
protected var hasSunroof: Boolean = false
protected var hasGPS: Boolean = false
abstract fun setBrand(brand: String): CarBuilder
abstract fun setModel(model: String): CarBuilder
abstract fun setColor(color: String): CarBuilder
abstract fun setEngineSize(size: Double): CarBuilder
abstract fun addSunroof(): CarBuilder
abstract fun addGPS(): CarBuilder
abstract fun build(): Car
}
// 3. ConcreteBuilder (具体建造者)
class ConcreteCarBuilder : CarBuilder() {
override fun setBrand(brand: String): CarBuilder = apply {
this.brand = brand
}
override fun setModel(model: String): CarBuilder = apply {
this.model = model
}
override fun setColor(color: String): CarBuilder = apply {
this.color = color
}
override fun setEngineSize(size: Double): CarBuilder = apply {
this.engineSize = size
}
override fun addSunroof(): CarBuilder = apply {
this.hasSunroof = true
}
override fun addGPS(): CarBuilder = apply {
this.hasGPS = true
}
override fun build(): Car {
// 构建前的验证
require(brand.isNotBlank()) { "必须指定汽车品牌" }
require(model.isNotBlank()) { "必须指定汽车型号" }
return Car(brand, model, color, engineSize, hasSunroof, hasGPS)
}
}
// 4. Director (指挥者) - 可选,定义构建流程
class CarDirector(private val builder: CarBuilder) {
fun constructSportsCar(): Car {
return builder
.setBrand("保时捷")
.setModel("911")
.setColor("红色")
.setEngineSize(3.0)
.addGPS()
.build()
}
fun constructFamilyCar(): Car {
return builder
.setBrand("丰田")
.setModel("凯美瑞")
.setColor("银色")
.setEngineSize(2.5)
.build()
}
}
三、Android中的建造者模式实现
1. 标准实现方式
// 使用Kotlin的简化实现(省略抽象建造者)
class User private constructor(
val name: String,
val age: Int,
val email: String?,
val phone: String?,
val address: String?
) {
// 建造者类
class Builder(private val name: String, private val age: Int) {
private var email: String? = null
private var phone: String? = null
private var address: String? = null
fun setEmail(email: String) = apply { this.email = email }
fun setPhone(phone: String) = apply { this.phone = phone }
fun setAddress(address: String) = apply { this.address = address }
fun build(): User {
// 参数验证
require(name.isNotBlank()) { "姓名不能为空" }
require(age >= 0) { "年龄不能为负数" }
email?.let {
require(it.contains("@")) { "邮箱格式不正确" }
}
return User(name, age, email, phone, address)
}
}
override fun toString(): String {
return "User(name='$name', age=$age, email=$email, phone=$phone, address=$address)"
}
}
// 使用示例
val user = User.Builder("张三", 25)
.setEmail("zhangsan@example.com")
.setPhone("13800138000")
.setAddress("北京市海淀区")
.build()
2. Android SDK中的建造者模式
// AlertDialog.Builder 是Android中最典型的建造者模式
val dialog = AlertDialog.Builder(context)
.setTitle("确认删除")
.setMessage("确定要删除这条记录吗?")
.setIcon(R.drawable.ic_warning)
.setPositiveButton("确定") { dialog, which ->
// 处理确定点击
}
.setNegativeButton("取消") { dialog, which ->
dialog.dismiss()
}
.setCancelable(false) // 点击外部不消失
.create()
// Notification.Builder (API 26+)
val notification = Notification.Builder(context, CHANNEL_ID)
.setContentTitle("新消息")
.setContentText("您有一条新消息")
.setSmallIcon(R.drawable.ic_notification)
.setPriority(Notification.PRIORITY_HIGH)
.setAutoCancel(true)
.build()
四、建造者模式的变体
1. 静态内部类建造者
class Product private constructor(
val id: Long,
val name: String,
val price: Double,
val description: String?,
val category: String
) {
// 静态内部类建造者
data class Builder(
var id: Long = 0,
var name: String = "",
var price: Double = 0.0,
var description: String? = null,
var category: String = "未分类"
) {
fun id(id: Long) = apply { this.id = id }
fun name(name: String) = apply { this.name = name }
fun price(price: Double) = apply { this.price = price }
fun description(description: String?) = apply { this.description = description }
fun category(category: String) = apply { this.category = category }
fun build() = Product(id, name, price, description, category)
}
}
// 使用:可以设置默认值
val product = Product.Builder()
.id(1L)
.name("智能手机")
.price(2999.0)
.category("电子产品")
.build()
2. DSL风格的建造者
// 使用Kotlin DSL特性创建更优雅的建造者
class Computer private constructor(
val cpu: String,
val ram: Int,
val storage: Int,
val gpu: String?,
val hasSSD: Boolean
) {
class Builder {
var cpu: String = "i5"
var ram: Int = 8
var storage: Int = 512
var gpu: String? = null
var hasSSD: Boolean = true
fun build(): Computer {
return Computer(cpu, ram, storage, gpu, hasSSD)
}
}
companion object {
// DSL风格构建方法
fun build(block: Builder.() -> Unit): Computer {
val builder = Builder()
builder.block()
return builder.build()
}
}
}
// 使用DSL风格
val gamingPC = Computer.build {
cpu = "i9-12900K"
ram = 32
storage = 2048
gpu = "RTX 4090"
hasSSD = true
}
3. 带验证的建造者
class Order private constructor(
val orderId: String,
val items: List<OrderItem>,
val totalAmount: Double,
val customerName: String,
val shippingAddress: String
) {
class Builder(private val orderId: String) {
private val items = mutableListOf<OrderItem>()
private var customerName: String = ""
private var shippingAddress: String = ""
fun addItem(name: String, price: Double, quantity: Int) = apply {
items.add(OrderItem(name, price, quantity))
}
fun customerName(name: String) = apply {
this.customerName = name
}
fun shippingAddress(address: String) = apply {
this.shippingAddress = address
}
fun build(): Order {
// 复杂验证逻辑
require(orderId.isNotBlank()) { "订单号不能为空" }
require(items.isNotEmpty()) { "订单必须包含至少一个商品" }
require(customerName.isNotBlank()) { "客户姓名不能为空" }
require(shippingAddress.isNotBlank()) { "收货地址不能为空" }
val totalAmount = items.sumOf { it.price * it.quantity }
require(totalAmount > 0) { "订单金额必须大于0" }
return Order(orderId, items.toList(), totalAmount, customerName, shippingAddress)
}
}
data class OrderItem(val name: String, val price: Double, val quantity: Int)
}
五、Android开发中的实际应用
1. 网络请求配置建造者
class HttpRequest private constructor(
val url: String,
val method: String,
val headers: Map<String, String>,
val body: String?,
val timeout: Long,
val retryCount: Int
) {
class Builder(private val url: String) {
private var method: String = "GET"
private val headers = mutableMapOf<String, String>()
private var body: String? = null
private var timeout: Long = 10000L // 10秒
private var retryCount: Int = 3
fun method(method: String) = apply {
require(method in listOf("GET", "POST", "PUT", "DELETE")) {
"不支持的HTTP方法: $method"
}
this.method = method
}
fun addHeader(key: String, value: String) = apply {
headers[key] = value
}
fun body(body: String) = apply {
require(method in listOf("POST", "PUT")) {
"只有POST和PUT请求可以有请求体"
}
this.body = body
}
fun timeout(timeout: Long) = apply {
require(timeout > 0) { "超时时间必须大于0" }
this.timeout = timeout
}
fun retryCount(count: Int) = apply {
require(count >= 0) { "重试次数不能为负数" }
this.retryCount = count
}
fun build(): HttpRequest {
require(url.startsWith("http")) { "URL必须以http或https开头" }
return HttpRequest(url, method, headers, body, timeout, retryCount)
}
}
fun execute(): String {
// 执行网络请求
return "Response from $url"
}
}
// 使用
val request = HttpRequest.Builder("https://api.example.com/data")
.method("POST")
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer token123")
.body("{\"key\": \"value\"}")
.timeout(15000L)
.retryCount(2)
.build()
val response = request.execute()
2. 数据库查询建造者
class QueryBuilder(private val tableName: String) {
private val columns = mutableListOf<String>()
private val conditions = mutableListOf<String>()
private var orderBy: String? = null
private var limit: Int? = null
private var offset: Int? = null
fun select(vararg cols: String) = apply {
columns.addAll(cols)
}
fun where(condition: String) = apply {
conditions.add(condition)
}
fun orderBy(column: String, ascending: Boolean = true) = apply {
orderBy = "$column ${if (ascending) "ASC" else "DESC"}"
}
fun limit(count: Int) = apply {
require(count > 0) { "限制数量必须大于0" }
limit = count
}
fun offset(count: Int) = apply {
require(count >= 0) { "偏移量不能为负数" }
offset = count
}
fun build(): String {
val columnList = if (columns.isEmpty()) "*" else columns.joinToString(", ")
var sql = "SELECT $columnList FROM $tableName"
if (conditions.isNotEmpty()) {
sql += " WHERE " + conditions.joinToString(" AND ")
}
orderBy?.let { sql += " ORDER BY $it" }
limit?.let { sql += " LIMIT $it" }
offset?.let { sql += " OFFSET $it" }
return sql
}
}
// 使用
val query = QueryBuilder("users")
.select("id", "name", "email")
.where("age > 18")
.where("status = 'active'")
.orderBy("created_at", ascending = false)
.limit(10)
.offset(0)
.build()
// 生成: SELECT id, name, email FROM users
// WHERE age > 18 AND status = 'active'
// ORDER BY created_at DESC LIMIT 10 OFFSET 0
六、建造者模式的优缺点
✅ 优点
- 良好的封装性:建造过程被封装在建造者中,客户端不需要知道内部细节
- 构造过程可控:可以精细控制对象的创建过程
- 代码可读性好:链式调用让代码更加清晰
- 易于扩展:增加新的构建步骤或创建新的具体建造者都很容易
- 参数验证:可以在build()方法中进行统一的参数验证
- 创建不可变对象:天然适合创建不可变对象
❌ 缺点
- 代码复杂度增加:需要创建多个类,增加了代码量
- 适用范围有限:主要适用于创建复杂对象
- 性能开销:相比直接构造,有额外的对象创建开销
- 可能过度设计:对于简单对象,使用建造者模式可能过于复杂
七、与相关模式的对比
建造者模式 vs 工厂模式
// 工厂模式:关注创建什么对象
interface CarFactory {
fun createCar(type: String): Car
}
// 建造者模式:关注如何创建对象
interface CarBuilder {
fun setBrand(brand: String): CarBuilder
fun setModel(model: String): CarBuilder
fun build(): Car
}
建造者模式 vs 构造器
// 传统构造器(参数多时难以使用)
class User(
name: String,
age: Int,
email: String? = null,
phone: String? = null,
address: String? = null,
gender: String? = null,
birthday: String? = null
)
// 建造者模式(更清晰)
User.Builder("张三", 25)
.setEmail("zhangsan@example.com")
.setPhone("13800138000")
.build()
八、Kotlin中的替代方案
1. 命名参数 + 默认参数
// Kotlin语言特性可以减少对建造者模式的需求
data class Person(
val name: String,
val age: Int,
val email: String? = null,
val phone: String? = null,
val address: String? = null
)
// 使用命名参数
val person = Person(
name = "李四",
age = 30,
email = "lisi@example.com",
phone = "13900139000"
)
2. apply函数简化
// 对于需要复杂初始化的对象,可以使用apply
val user = User("王五", 28).apply {
email = "wangwu@example.com"
phone = "13700137000"
// 其他复杂初始化逻辑
}
3. DSL + 类型安全构建器
// 创建HTML DSL
fun html(block: HTML.() -> Unit): HTML {
val html = HTML()
html.block()
return html
}
class HTML {
private val children = mutableListOf<Element>()
fun head(block: Head.() -> Unit) {
children.add(Head().apply(block))
}
fun body(block: Body.() -> Unit) {
children.add(Body().apply(block))
}
}
// 使用
val page = html {
head {
title = "My Page"
}
body {
h1 { "Welcome" }
p { "This is a paragraph" }
}
}
九、最佳实践指南
何时使用建造者模式?
✅ 适用场景:
- 对象有很多参数(超过4个)
- 有些参数是可选的或有默认值
- 需要创建不可变对象
- 对象创建需要复杂的验证逻辑
- 需要创建不同表示的对象
- 需要清晰的构建步骤
❌ 避免使用场景:
- 对象参数很少(少于3个)
- 所有参数都是必需的
- 对象创建很简单
- 使用Kotlin且参数有合理的默认值
Android开发建议
- 优先使用Kotlin特性:命名参数+默认参数可以替代简单的建造者
- 复杂配置使用建造者:如网络请求配置、数据库查询等
- 保持建造者简洁:避免在建造者中加入业务逻辑
- 合理使用DSL:对于配置类的对象,DSL可以提供更好的API
- 考虑使用工厂方法:如果对象创建逻辑简单,工厂方法可能更合适
十、总结
建造者模式在Android开发中是非常有用的设计模式,特别是在以下场景:
- 创建复杂配置对象:如AlertDialog、Notification等
- 构建不可变对象:确保对象状态不变
- 提供流畅API:改善API设计,提高代码可读性
- 参数验证:集中进行参数验证
在Kotlin中,由于语言特性的增强(命名参数、默认参数、apply函数、DSL),有些传统的建造者模式用例可以通过更简洁的方式实现。但是,对于复杂的对象创建过程,建造者模式仍然是不可替代的工具。
关键点:
- 使用建造者模式创建不可变对象
- 在build()方法中进行参数验证
- 考虑使用链式调用提供流畅API
- 在Android中合理使用,避免过度设计
建造者模式是创建型模式中的重要一员,合理使用可以显著提高代码的可维护性和可读性