減小Kotlin when 對字段的嵌套方案思考

爲何會有這樣的想法?

最近開發新項目時遇到一個問題,項目中對與許多資源的字段嵌套層級不少,可是字段又都從一個接口返回,而且存在與一個JSON對象中。帶來的問題:安全

  • when 嵌套層級過多
  • when 語句會有else得狀況,代碼不夠安全
  • 後期維護成本很高

雖然使用kotlin,語法上已經能精簡不少,可是這種需求不免讓代碼的可讀性,維護性不好服務器

實際的需求場景

對於用戶資源的展現。首先資源類型分爲:Image,Video,而後資源的類型分爲normal,red,burning,redBurning,狀態分爲read,noRead。而服務器返回的相關字段都存儲在一個JSON對象中,而且都屬於同一級的屬性。那麼能夠獲得這樣的一個data classide

data class ResBean(val url: String, val format: String, val type: String, val viewStatus: String){
            companion object { // 在伴生對象中定義一些常量來表示其中的字段類型
            const val RES_FORMAT_VIDEO = "video"
            const val RES_FORMAT_IMAGE = "image"
            const val RES_RORMAT_UNKONW = "unKnow"
    
            const val RES_TYPE_NORMAL = "normal"
            const val RES_TYPE_RED = "red"
            const val RES_TYPE_RED_BURNING = "red_burning"
            const val RES_TYPE_BURNING = "burning"
        }
    }
複製代碼

使用時的需求場景,如今須要在一個ViewPager中展現這些資源,首先須要考慮到時Format,其次還須要考慮到Type,那麼你就會獲得一個when嵌套者when的代碼塊url

fun showByFormatAndType(res: ResBean) {
    when (res.format) {
        ResBean.RES_FORMAT_IMAGE ->
            when (res.type) {
                ResBean.RES_TYPE_RED -> showRedImage()
                ResBean.RES_TYPE_BURNING -> showBurningImage()
                ResBean.RES_TYPE_RED_BURNING -> showRedBurningImage()
                ResBean.RES_TYPE_NORMAL -> showNormalImage()
            }
        ResBean.RES_FORMAT_VIDEO ->
            when (res.type) {
                ResBean.RES_TYPE_RED -> showRedVideo()
                ResBean.RES_TYPE_BURNING -> showBurningVideo()
                ResBean.RES_TYPE_RED_BURNING -> showRedBurningVideo()
                ResBean.RES_TYPE_NORMAL -> showNormalVideo()
            }
        else -> showUnKown()
    }
}
複製代碼

這樣的代碼帶來的困擾其實主要時易讀性後後期維護上困難,若是你前期沒有作很好的提出封裝,那麼就更痛苦了。試想一下若是須要添加一個新的format,或者一個type都不太舒服。並且你還得考慮不少得else狀況spa

實現方案

在層級嵌套上,能夠考慮將fromat和type作一個積類型,也就合二爲一來減小多層嵌套,代碼以下:code

fun byFormatAndTypeTodo(res: ResBean) {
    with(res) {
        when {
            format == ResBean.RES_FORMAT_IMAGE && type == ResBean.RES_TYPE_NORMAL -> showNormalImage()
            format == ResBean.RES_FORMAT_IMAGE && type == ResBean.RES_TYPE_RED -> showRedImage()
            format == ResBean.RES_FORMAT_IMAGE && type == ResBean.RES_TYPE_BURNING -> showBurningImage()
            format == ResBean.RES_FORMAT_IMAGE && type == ResBean.RES_TYPE_RED_BURNING -> showRedBurningImage()
            format == ResBean.RES_FORMAT_VIDEO && type == ResBean.RES_TYPE_NORMAL -> showNormalVideo()
            format == ResBean.RES_FORMAT_VIDEO && type == ResBean.RES_TYPE_RED -> showRedVideo()
            format == ResBean.RES_FORMAT_VIDEO && type == ResBean.RES_TYPE_BURNING -> showBurningVideo()
            format == ResBean.RES_FORMAT_VIDEO && type == ResBean.RES_TYPE_RED_BURNING -> showRedBurningVideo()
            else -> showUnKown()
        }
    }
}
複製代碼

這樣一來減小了層級得嵌套,可是犧牲了代碼得可讀性,最後考慮結合密封來提高可讀性。首先定義一個密封類orm

sealed class ResWithFormatAndType {
    data class NormalImageRes(val res: ResBean) : ResWithFormatAndType()
    data class RedImageRes(val res: ResBean) : ResWithFormatAndType()
    data class BurningImageRes(val res: ResBean) : ResWithFormatAndType()
    data class RedAndBurningImageRes(val res: ResBean) : ResWithFormatAndType()
    data class NormalVideoRes(val res: ResBean) : ResWithFormatAndType()
    data class RedVideoRes(val res: ResBean) : ResWithFormatAndType()
    data class BurningVideoRes(val res: ResBean) : ResWithFormatAndType()
    data class RedAndBurningVideoRes(val res: ResBean) : ResWithFormatAndType()
    object UnkownRes : ResWithFormatAndType()
}
複製代碼

以後封裝一個方法來獲取相應對象

fun getResByFormatWithType(res: ResBean): ResWithFormatAndType = with(res) {
    when {
        format == ResBean.RES_FORMAT_IMAGE && type == ResBean.RES_TYPE_NORMAL -> NormalImageRes(res)
        format == ResBean.RES_FORMAT_IMAGE && type == ResBean.RES_TYPE_RED -> RedImageRes(res)
        format == ResBean.RES_FORMAT_IMAGE && type == ResBean.RES_TYPE_BURNING -> BurningImageRes(res)
        format == ResBean.RES_FORMAT_IMAGE && type == ResBean.RES_TYPE_RED_BURNING -> RedAndBurningImageRes(res)
        format == ResBean.RES_FORMAT_VIDEO && type == ResBean.RES_TYPE_NORMAL -> NormalVideoRes(res)
        format == ResBean.RES_FORMAT_VIDEO && type == ResBean.RES_TYPE_RED -> RedVideoRes(res)
        format == ResBean.RES_FORMAT_VIDEO && type == ResBean.RES_TYPE_BURNING -> BurningVideoRes(res)
        format == ResBean.RES_FORMAT_VIDEO && type == ResBean.RES_TYPE_RED_BURNING -> RedAndBurningVideoRes(res)
        else -> ResWithFormatAndType.UnkownRes
    }
}
複製代碼

最後在使用得地方接口

fun test() {
    when (getResWithFormatAndType(resBean) {
        is ResWithFormatAndType.NormalImageRes -> showNormalImage()
        is ResWithFormatAndType.BurningImageRes -> showBurningImage()
        is ResWithFormatAndType.RedImageRes -> showRedImage()
        is ResWithFormatAndType.RedAndBurningImageRes -> showRedBurningImage()
        is ResWithFormatAndType.BurningVideoRes -> showBurningVideo()
        is ResWithFormatAndType.NormalVideoRes -> showNormalVideo()
        is ResWithFormatAndType.RedAndBurningVideoRes -> showRedBurningVideo()
        is ResWithFormatAndType.RedVideoRes -> showRedVideo()
    }
}
複製代碼

最終解決後的方案我的以爲還不夠完善,之前看到過Scala有模式匹配這樣的概念,可是在Kotlin中沒有找到最終的解決方案,也期許一下Kotlin能早日支持模式匹配這樣的概念。資源

相關文章
相關標籤/搜索