文章目录
- User的构建
- 子属性的Builder实现
- Address类
- Company的Builder
- Contact的Builder
- Education的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