建造者模式解决的就是将一个复杂对象的构建与它的表示分离,从而使对象的构建过程能有不一样的表示。什么意思?java
fun main(args: Array<String>) { MacBook(15,8,16,512,true,"test","") } class MacBook(private var screenSize:Int, private var cpuCore:Int, private var ramCapacity:Int, private var diskCapacity:Int, private var appleCare:Boolean, private var engraveWords:String, private var protectingShell:String) 复制代码
面对这种过长的参数构造函数,咱们须要经过建造者模式来改造。web
无论是以前提到的工厂模式仍是单例,都没解决扩展大量可选参数的问题,例如上述咱们在购买MacBook的时候都会进行可选产品定制,例如屏幕尺寸,处理器种类 ,内存大小,硬盘大小 ,Applecare,铭刻内容,配件...等等等数组
在业务中咱们也常常使用重叠构造器(telescoping constructor),先提供一个只有必要参数的构造函数,在提供其余不一样参数组合的构造,可是若是参数过长是不利于维护的。因此咱们用建造者模式改造:安全
fun main(args: Array<String>) { MacBook.Builder() .setScreenSize(15) .setCpuCore(8) .setRamCapacity(16) .setDiskCapacity(512) .setAppleCare(true) .build() } class MacBook private constructor(var screenSize: Int, var cpuCore: Int, var ramCapacity: Int, var diskCapacity: Int, var appleCare: Boolean) { class Builder(private var screenSize: Int? = null, private var cpuCore: Int? = null, private var ramCapacity: Int? = null, private var diskCapacity: Int? = null, private var appleCare: Boolean = false) { fun setScreenSize(screenSize: Int?): Builder { this.screenSize = screenSize return this } fun setCpuCore(cpuCore: Int?): Builder { this.cpuCore = cpuCore return this } fun setRamCapacity(ramCapacity: Int?): Builder { this.ramCapacity = ramCapacity return this } fun setDiskCapacity(diskCapacity: Int?): Builder { this.diskCapacity = diskCapacity return this } fun setAppleCare(appleCare: Boolean): Builder { this.appleCare = appleCare return this } fun build(): MacBook { return MacBook(screenSize!!, cpuCore!!, ramCapacity!!, diskCapacity!!, appleCare) } } } 复制代码
步骤:bash
这样比直接在构造函数中配置参数优雅了很多,建立实例时,只须要在对set方法进行赋值便可。markdown
不足:app
这是Java中典型的建造者模式,但在kotlin中咱们其实能够尽可能避免使用它,由于建造者模式本质上也是模拟了具名的可选参数 ,例如Flutter中的Dart使用 { }
来包裹具名参数传递的时候,和参数顺序无关,固然kotlin也支持这种特性函数
void printTest(String name1, {String name2, String name3}) { } 复制代码
kotlin函数与构造器都支持具名可选参数,咱们直接利用data class声明一个数据类 :ui
fun main(args: Array<String>) { MacBook(screenSize = 15) MacBook(appleCare = true ,screenSize = 15,cpuCore = 8) } data class MacBook(val screenSize: Int? = null, val cpuCore: Int? = null, val ramCapacity: Int? = null, val diskCapacity: Int? = null, val appleCare: Boolean? = null) 复制代码
这样咱们声明对象时,每一个参数名都是显式的,不用按照顺序定义参数内容,而且参数都由val声明更加安全this
为了让业务更加安全,咱们常常会利用建造者模式中的属性进行业务行为约束
fun main(args: Array<String>) { MacBook(cpuCore = 38) } class MacBook(val screenSize: Int? = null, val cpuCore: Int? = null, val ramCapacity: Int? = null, val diskCapacity: Int? = null, val appleCare: Boolean? = null) { init { require(cpuCore!! <= 32) { "CPU核心数超过定制需求" } } } 复制代码
这样当咱们传入的参数定义符合requair中的约束,就会抛出异常:
Exception in thread "main" java.lang.IllegalArgumentException: CPU核心数超过定制需求 复制代码
固然这为了说明requair示例,具体这种业务仍是须要数据配置例如枚举,自定义注解等进行详细约束的
这里多说一句,上述requair没有使用data class,Kotlin中带有数据类的模型已经比Java同类的模型更精简和简洁。用一个data关键字抽象了全部参数的getters, setters、toString()和copy()方法,使咱们的数据类可以反映出他们惟一须要关注的事情—数据模型独立。
因此咱们在业务开发时能够借助Kotlin的扩展将业务或转换逻辑与数据模型分离,例如在咱们在企业级IM应用中须要对用户页面或者访问内容进行动态分发
data class RongCloudCast( val uuid: String, val departments: String, val category: String, val website: String, val version: String, val hashCode: String ) { // 路由跳转基础路径 fun getRouterBaseUrl(): String = "$departments/$category" // 容许传递给WebView进程的URL fun getWebViewEntryUrl(): String = "https://$website/$version/$hashCode" } 复制代码
相较于这种仍是由扩展功能完成拼接逻辑
data class RongCloudCast( val uuid: String, val departments: String, val category: String, val website: String, val version: String, val hashCode: String ) fun RongCloudCast.getRouterBaseUrl(): String = "$departments/$category" fun RongCloudCast.getWebViewEntryUrl(): String = "https://$website/$version/$hashCode" 复制代码