文章目录
- 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?, // optionallastName: String?,address: Address?,contact: Contact?, // optionalcompany: Company?, // optionaleducations: List<Education>,
) {val firstName: Stringval middleName: String? // optionalval lastName: Stringval address: Addressval contact: Contact? // optionalval company: Company? // optionalval 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 = firstNamethis.middleName = middleNamethis.lastName = lastNamethis.address = addressthis.contact = contactthis.company = companythis.educations = educations}class Builder {private var firstName: String? = nullprivate var middleName: String? = null // optionalprivate var lastName: String? = nullprivate var address: Address? = nullprivate var contact: Contact? = null // optionalprivate var company: Company? = null // optionalprivate val educations = mutableListOf<Education>()fun setFirstName(firstName: String): Builder {this.firstName = firstNamereturn this}// ORfun 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: Stringval line2: String?val city: Stringval state: Stringval country: Stringval pinCode: Intinit {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 = line1this.line2 = line2this.city = citythis.state = statethis.country = countrythis.pinCode = pinCode}class Builder {private var line1: String? = nullprivate var city: String? = nullprivate var state: String? = nullprivate var country: String? = nullprivate var pinCode: Int? = nullprivate var line2: String? = nullfun 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: Stringinit {if (name.isNullOrBlank())throw IllegalArgumentException("Name must not be null or blank.")this.name = name}class Builder {private var name: String? = nullfun 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: Stringval githubHandle: Stringval phoneNumber: Stringval email: Stringinit {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: Stringval yearOfPassing: Intval degree: String? // optionalinit {if (school.isBlank())throw IllegalArgumentException("School must not be blank.")if (yearOfPassing == null) {throw IllegalArgumentException("School must not be blank.")}this.school = schoolthis.yearOfPassing = yearOfPassingthis.degree = degree}
}
class EducationBuilder {private var school: String = ""private var yearOfPassing: Int? = nullprivate var degree: String? = null // optionalfun 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 hereval user1 = User.Builder().setFirstName("Abhishek").setLastName("Saxena").setAddress(address).setCompany(company).addEducation(educations) // <- a list of education is added.build() // <- user object is built hereval 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