Android設計模式(三) 觀察者模式

Android開發中的基於觀察者模式實現的設計仍是不少的,好比rxjava、LiveData...常見的按鈕點擊事件html

  • Button ---> 被觀察者
  • OnClickListener ---> 觀察者
  • setOnClickListener() ---> 訂閱
  • OnClick() ---> 事件

觀察者模式定義可一對多的依賴關係,讓多個觀察者同時監聽某一個對象,當這個主體對象在狀態上發生變化時,會通知全部觀察者對象 ,使他們能自動更新本身,叫法也有不少,發佈-訂閱(Publish-Subscribe)、模型-視圖(Model-View)等等java

Java對於觀察者模式在java.util包中提供了Observer接口和Observable抽象類。註冊,刪除,通知觀察者等功能已內置。kotlin一樣支持使用Java中提供的觀察者模式bash

例如電商項目中的動態更新價格markdown

// 被觀察者
class PriceSubject : Observable() {
    // 定義一組觀察者對象
    private val observers = mutableSetOf<Observer>()
    // 訂閱
    fun subject(ob: Observer) {
        observers.add(ob)
    }
    // 解除訂閱
    fun UnSubject(ob: Observer) {
        observers.remove(ob)
    }
    // 價格賦值
    fun setPrice(price:Int ){
            notify(price)
    }
    //更新數據
    private fun <T : Any?> notify(msg: T) {
        for (ob in observers) {
            ob.update(this,msg)
        }
    }
}

// 觀察者
class PriceOb(private val channel:String):Observer{
    override fun update(o: Observable?, price: Any?) {
       if (o is PriceSubject){
           print("接收賦值-觀察者:$channel - 價格:$price\n")
       }
    }

}

class PriceOb1 :Observer by PriceOb("第三方")

fun main(args: Array<String>) {
    PriceSubject().apply {
         // 訂閱
        subject(PriceOb("自營"))
        subject(PriceOb1())
        setPrice(100)
    }
}
複製代碼
接收賦值-觀察者:自營 - 價格:100
接收賦值-觀察者:第三方 - 價格:100
複製代碼

委託屬性

kotlin中的委託屬性官方文檔 其中咱們須要的是app

可觀察屬性(observable properties): 監聽器會收到有關此屬性變動的通知ide

inline fun <T> observable(
    initialValue: T,
    crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit
): ReadWriteProperty<Any?, T>
複製代碼

如果咱們賦予價格屬性更多定義,咱們須要發佈者對外提供一個API接口而不是在實現Observer接口的類中區分不一樣的邏輯。oop

Delegates.observable()中提供了表明委託屬性三個參數:元數據property: KProperty<*>對象,新舊值。this

// 觀察者
interface PriceChangedListener {
    // 定義一系列事件

    //
    fun onNormalPrice(price: Price)

    // 秒殺價
    fun onSpikePrice(price: Price)

    // 預購價
    fun onAdvancePrice(price: Price)

    // 會員價
    fun onVIPPrice(price: Price)
    //
}

class PriceObserver : PriceChangedListener {
    override fun onNormalPrice(price: Price) {
        print("常規價${price.newPrice}")
    }

    override fun onSpikePrice(price: Price) {
        print("秒殺價${price.newPrice}")
    }

    override fun onAdvancePrice(price: Price) {
        print("預購價${price.newPrice}")
    }

    override fun onVIPPrice(price: Price) {
        print("會員價${price.newPrice}")
    }

}

data class Price(val newPrice: Int, //新價格
                 val oldPrice: Int, //舊價格
                 val type: Int,      //價格屬性類型
                 val discount: Boolean //是否能抵用折扣
)

// 被觀察者
class PriceSubject {
    // 觀察者對象
    private var listeners = mutableSetOf<PriceChangedListener>()

    fun subject(ob: PriceChangedListener) {
        listeners.add(ob)
    }

    fun unSubject(ob: PriceChangedListener) {
        listeners.remove(ob)
    }

    var price: Price by Delegates.observable(Price(0, 0, 0, false)) { property, oldValue, newValue ->
        listeners.forEach {
            when (newValue.type) {
                0 -> it.onNormalPrice(newValue)
                1 -> it.onSpikePrice(newValue)
                2 -> it.onAdvancePrice(newValue)
                3 -> it.onVIPPrice(newValue)
            }
        }

    }

}

fun main(args: Array<String>) {
    PriceSubject().apply {
        subject(PriceObserver())
        price = Price(199, 0, 0, false)
        price = Price(99, 100, 3, false)
    }
}
複製代碼

Vetoable

inline fun <T> vetoable(
    initialValue: T,
    crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean
): ReadWriteProperty<Any?, T>
複製代碼

在觀察者模式中,若是咱們不想被觀察的值被任意的修改,能夠利用Vetoable在新的值被賦值生效以前進行截獲,而後根據相關業務邏輯處理它。spa

var price: Price by Delegates.vetoable(Price(0, 0, 0, false)) { property, oldValue, newValue ->
        listeners.forEach {
            when (newValue.type) {
                0 -> it.onNormalPrice(newValue)
                1 -> it.onSpikePrice(newValue)
                2 -> it.onAdvancePrice(newValue)
                3 -> it.onVIPPrice(newValue)
            }
        }
        // 添加自定義規則
        if (newValue.type==3&&newValue.oldPrice < newValue.newPrice)
            true else throw IllegalArgumentException("會員價格不合理")

    }
複製代碼
相關文章
相關標籤/搜索