一个标准的Builder模式写法示例


本文以一个例子介绍了 Builder 模式的标准写法。
比如,有一个复杂的对象User,其中的属性也包括几个对象Address、Contact、Company、List。在构建这种对象时,使用Builder模式该如何实现呢?

User的构建

class User private constructor(
    firstName: String?,
    middleName: String?, // optional
    lastName: String?,
    address: Address?,
    contact: Contact?, // optional
    company: Company?, // optional
    educations: List<Education>,
) {
    val firstName: String
    val middleName: String? // optional
    val lastName: String
    val address: Address
    val contact: Contact? // optional
    val company: Company? // optional
    val educations: List<Education>

    init {
        if (firstName.isNullOrBlank())
            throw IllegalArgumentException("First name is required")

        if (lastName.isNullOrBlank())
            throw IllegalArgumentException("Last name is required")

        if (address == null)
            throw IllegalArgumentException("Address is required")

        if (educations.isEmpty())
            throw IllegalArgumentException("Education list is required")

        this.firstName = firstName
        this.middleName = middleName
        this.lastName = lastName
        this.address = address
        this.contact = contact
        this.company = company
        this.educations = educations
    }

    class Builder {
        private var firstName: String? = null
        private var middleName: String? = null // optional
        private var lastName: String? = null
        private var address: Address? = null
        private var contact: Contact? = null // optional
        private var company: Company? = null // optional
        private val educations = mutableListOf<Education>()

        fun setFirstName(firstName: String): Builder {
            this.firstName = firstName
            return this
        }

        // OR

        fun setMiddleName(middleName: String) = apply {
            this.middleName = middleName
        }

        fun setLastName(lastName: String) = apply {
            this.lastName = lastName
        }

        fun setAddress(address: Address) = apply {
            this.address = address
        }

        fun setContact(contact: Contact) = apply {
            this.contact = contact
        }

        fun setCompany(company: Company) = apply {
            this.company = company
        }

        fun addEducation(education: Education) = apply {
            this.educations.add(education)
        }

        fun addEducation(educations: List<Education>) = apply {
            this.educations.addAll(educations)
        }

        fun setEducations(educations: List<Education>) = apply {
            this.educations.clear()
            this.educations.addAll(educations)
        }

        fun build(): User {
            return User(
                firstName,
                middleName,
                lastName,
                address,
                contact,
                company,
                educations
            )
        }
    }
}

子属性的Builder实现

其他子属性也是同理

Address类

class Address(
    line1: String?,
    line2: String?,
    city: String?,
    state: String?,
    country: String?,
    pinCode: Int?
) {

    val line1: String
    val line2: String?
    val city: String
    val state: String
    val country: String
    val pinCode: Int

    init {
        if (line1.isNullOrBlank())
            throw IllegalArgumentException("Line1 must not be null or blank.")

        if (city.isNullOrBlank())
            throw IllegalArgumentException("City must not be null or blank.")
        if (state.isNullOrBlank())
            throw IllegalArgumentException("State must not be null or blank.")
        if (country.isNullOrBlank())
            throw IllegalArgumentException("Country must not be null or blank.")
        if (pinCode == null)
            throw IllegalArgumentException("Pin code must not be null.")

        this.line1 = line1
        this.line2 = line2
        this.city = city
        this.state = state
        this.country = country
        this.pinCode = pinCode
    }

    class Builder {
        private var line1: String? = null
        private var city: String? = null
        private var state: String? = null
        private var country: String? = null
        private var pinCode: Int? = null
        private var line2: String? = null

        fun setLine1(line1: String?) = apply {
            this.line1 = line1
        }

        fun setLine2(line2: String?) = apply {
            this.line2 = line2
        }

        fun setCity(city: String?) = apply {
            this.city = city
        }

        fun setState(state: String?) = apply {
            this.state = state
        }

        fun setCountry(country: String?) = apply {
            this.country = country
        }

        fun setPinCode(pinCode: Int) = apply {
            this.pinCode = pinCode
        }

        fun build(): Address {
            return Address(
                line1 = line1,
                line2 = line2,
                city = city,
                state = state,
                country = country,
                pinCode = pinCode
            )
        }
    }
}

Company的Builder

class Company(
    name: String?
) {

    val name: String

    init {
        if (name.isNullOrBlank())
            throw IllegalArgumentException("Name must not be null or blank.")

        this.name = name
    }

    class Builder {
        private var name: String? = null

        fun setName(name: String) = apply {
            this.name = name
        }

        fun build(): Company {
            return Company(name)
        }
    }
}

Contact的Builder

class Contact(
    twitterHandle: String,
    githubHandle: String,
    phoneNumber: String,
    email: String,
) {
    val twitterHandle: String
    val githubHandle: String
    val phoneNumber: String
    val email: String

    init {
        this.twitterHandle = twitterHandle.ifBlank {
            throw IllegalArgumentException("Twitter handle must not be blank.")
        }

        this.githubHandle = githubHandle.ifBlank {
            throw IllegalArgumentException("GitHub handle must not be blank.")
        }

        this.phoneNumber = phoneNumber.ifBlank {
            throw IllegalArgumentException("Phone number must not be blank.")
        }

        this.email = email.ifBlank {
            throw IllegalArgumentException("Email must not be blank.")
        }
    }

    class Builder {
        private var twitterHandle: String = ""
        private var githubHandle: String = ""
        private var phoneNumber: String = ""
        private var email: String = ""

        fun setTwitterHandle(twitterHandle: String) = apply {
            this.twitterHandle = twitterHandle
        }

        fun setGithubHandle(githubHandle: String) = apply {
            this.githubHandle = githubHandle
        }

        fun setPhoneNumber(phoneNumber: String) = apply {
            this.phoneNumber = phoneNumber
        }

        fun setEmail(email: String) = apply {
            this.email = email
        }

        fun build(): Contact {
            return Contact(
                twitterHandle = twitterHandle,
                githubHandle = githubHandle,
                phoneNumber = phoneNumber,
                email = email
            )
        }
    }
}

Education的Builder

这个特殊点,Builder写到类外边。

class Education(
    school: String,
    yearOfPassing: Int?,
    degree: String? // optional
) {
    val school: String
    val yearOfPassing: Int
    val degree: String? // optional

    init {

        if (school.isBlank())
            throw IllegalArgumentException("School must not be blank.")

        if (yearOfPassing == null) {
            throw IllegalArgumentException("School must not be blank.")
        }

        this.school = school
        this.yearOfPassing = yearOfPassing
        this.degree = degree
    }
}
class EducationBuilder {
    private var school: String = ""
    private var yearOfPassing: Int? = null
    private var degree: String? = null // optional

    fun setSchool(school: String): EducationBuilder = apply {
        this.school = school
    }

    fun setYearOfPassing(yearOfPassing: Int?): EducationBuilder = apply {
        this.yearOfPassing = yearOfPassing
    }

    fun setDegree(degree: String?): EducationBuilder = apply {
        this.degree = degree
    }

    fun build(): Education {
        return Education(school, yearOfPassing, degree)
    }
}

使用举例

fun main() {
    val address = getAddress()
    val company = getCompany()
    val contact = getContact()
    
    val schoolEducation = getSchoolEducation()
    val universityEducation = getUniversityEducation()
    
    val educations = listOf(schoolEducation, universityEducation)

    val user = User.Builder()
        .setFirstName("Abhishek")
        .setLastName("Saxena")
        .setAddress(address)
        .setCompany(company)
        .setContact(contact)
        .setEducations(educations) // <- a list of education is set
        .build() // <- user object is built here

    val user1 = User.Builder()
        .setFirstName("Abhishek")
        .setLastName("Saxena")
        .setAddress(address)
        .setCompany(company)
        .addEducation(educations) // <- a list of education is added
                .build() // <- user object is built here

    val user2 = User.Builder()
        .setFirstName("Abhishek")
        .setLastName("Saxena")
        .setAddress(address)
        .addEducation(schoolEducation)
        .addEducation(universityEducation) // <- Education is added one at a time
        .build() // <- user object is built here
}

private fun getAddress(): Address = Address.Builder()
    .setLine1("test")
    .setCity("Delhi")
    .setState("Delhi")
    .setCountry("India")
    .setPinCode(123456)
    .build()

private fun getCompany(): Company = Company.Builder()
    .setName("ABC")
    .build()

private fun getContact(): Contact = Contact.Builder()
    .setEmail("abc@def.com")
    .build()

private fun getSchoolEducation(): Education = EducationBuilder()
    .setSchool("ABC School")
    .setYearOfPassing(2014)
    .build()

private fun getUniversityEducation(): Education = EducationBuilder()
    .setSchool("ABC University")
    .setDegree("B.Tech")
    .setYearOfPassing(2020)
    .build()

好处

  • 数据类本身构造方法为 private 避免了外界直接创建,限制只能通过Builder模式创建
  • 数据类里的属性具有不可修改性,保障了数据的一致性
  • 通过set方法灵活组装属性
  • 数据类的 init 代码块里对数据做了校验,不合规的情况抛出异常,避免了不合规数据的存在
  • 外界可以链式调用,优雅的创建处对象

参考文档:https://proandroiddev.com/builder-design-pattern-in-kotlin-c52e41bd6020

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子林Android

感谢老板,老板大气!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值