Android設計模式(二) 建造者模式

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

  • 定義嵌套類Builder負責建立MacBook對象
  • 私有化類MacBook的構造函數,確保沒法直接經過MacBook聲明實例
  • 經過在Builder類中定義的可選屬性來定製對象建立的內容
  • 調用build()方法返回一個MacBook實例

這樣比直接在構造函數中配置參數優雅了很多,建立實例時,只須要在對set方法進行賦值便可。markdown

不足:app

  • 業務需求參數很是多時,代碼依舊會顯得比較長
  • build()方法常常會忘記調用
  • 建立實例時必須先建立其構造器。

這是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

利用requair進行參數行爲約束

爲了讓業務更加安全,咱們常常會利用建造者模式中的屬性進行業務行爲約束

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"
複製代碼
相關文章
相關標籤/搜索