Android 建造者模式浅析

9 阅读6分钟

一、基本概念

定义

建造者模式将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。

核心思想

  1. 分离构造逻辑:将对象的构造过程分解为多个步骤
  2. 链式调用:提供流畅的API接口
  3. 参数验证:在最终构建时进行参数验证
  4. 不可变对象:通常构建不可变对象

二、基本结构

四个核心角色

// 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

六、建造者模式的优缺点

✅ 优点

  1. 良好的封装性:建造过程被封装在建造者中,客户端不需要知道内部细节
  2. 构造过程可控:可以精细控制对象的创建过程
  3. 代码可读性好:链式调用让代码更加清晰
  4. 易于扩展:增加新的构建步骤或创建新的具体建造者都很容易
  5. 参数验证:可以在build()方法中进行统一的参数验证
  6. 创建不可变对象:天然适合创建不可变对象

❌ 缺点

  1. 代码复杂度增加:需要创建多个类,增加了代码量
  2. 适用范围有限:主要适用于创建复杂对象
  3. 性能开销:相比直接构造,有额外的对象创建开销
  4. 可能过度设计:对于简单对象,使用建造者模式可能过于复杂

七、与相关模式的对比

建造者模式 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" }
    }
}

九、最佳实践指南

何时使用建造者模式?

✅ 适用场景

  1. 对象有很多参数(超过4个)
  2. 有些参数是可选的或有默认值
  3. 需要创建不可变对象
  4. 对象创建需要复杂的验证逻辑
  5. 需要创建不同表示的对象
  6. 需要清晰的构建步骤

❌ 避免使用场景

  1. 对象参数很少(少于3个)
  2. 所有参数都是必需的
  3. 对象创建很简单
  4. 使用Kotlin且参数有合理的默认值

Android开发建议

  1. 优先使用Kotlin特性:命名参数+默认参数可以替代简单的建造者
  2. 复杂配置使用建造者:如网络请求配置、数据库查询等
  3. 保持建造者简洁:避免在建造者中加入业务逻辑
  4. 合理使用DSL:对于配置类的对象,DSL可以提供更好的API
  5. 考虑使用工厂方法:如果对象创建逻辑简单,工厂方法可能更合适

十、总结

建造者模式在Android开发中是非常有用的设计模式,特别是在以下场景:

  1. 创建复杂配置对象:如AlertDialog、Notification等
  2. 构建不可变对象:确保对象状态不变
  3. 提供流畅API:改善API设计,提高代码可读性
  4. 参数验证:集中进行参数验证

在Kotlin中,由于语言特性的增强(命名参数、默认参数、apply函数、DSL),有些传统的建造者模式用例可以通过更简洁的方式实现。但是,对于复杂的对象创建过程,建造者模式仍然是不可替代的工具。

关键点

  • 使用建造者模式创建不可变对象
  • 在build()方法中进行参数验证
  • 考虑使用链式调用提供流畅API
  • 在Android中合理使用,避免过度设计

建造者模式是创建型模式中的重要一员,合理使用可以显著提高代码的可维护性和可读性