建造者模式解決的就是將一個複雜對象的構建與它的表示分離,從而使對象的構建過程能有不一樣的表示。什麼意思?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" 複製代碼