Android設(shè)計(jì)模式(三) 觀察者模式

Android開發(fā)中的基于觀察者模式實(shí)現(xiàn)的設(shè)計(jì)還是很多的,比如rxjava镣奋、LiveData...常見的按鈕點(diǎn)擊事件

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

觀察者模式定義可一對(duì)多的依賴關(guān)系后豫,讓多個(gè)觀察者同時(shí)監(jiān)聽某一個(gè)對(duì)象悉尾,當(dāng)這個(gè)主體對(duì)象在狀態(tài)上發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象 挫酿,使他們能自動(dòng)更新自己构眯,叫法也有很多,發(fā)布-訂閱(Publish-Subscribe)早龟、模型-視圖(Model-View)等等

Java對(duì)于觀察者模式在java.util包中提供了Observer接口和Observable抽象類惫霸。注冊(cè),刪除,通知觀察者等功能已內(nèi)置。kotlin同樣支持使用Java中提供的觀察者模式

例如電商項(xiàng)目中的動(dòng)態(tài)更新價(jià)格

// 被觀察者
class PriceSubject : Observable() {
    // 定義一組觀察者對(duì)象
    private val observers = mutableSetOf<Observer>()
    // 訂閱
    fun subject(ob: Observer) {
        observers.add(ob)
    }
    // 解除訂閱
    fun UnSubject(ob: Observer) {
        observers.remove(ob)
    }
    // 價(jià)格賦值
    fun setPrice(price:Int ){
            notify(price)
    }
    //更新數(shù)據(jù)
    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 - 價(jià)格:$price\n")
       }
    }

}

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

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

委托屬性

kotlin中的委托屬性官方文檔
其中我們需要的是

可觀察屬性(observable properties): 監(jiān)聽器會(huì)收到有關(guān)此屬性變更的通知

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

若是我們賦予價(jià)格屬性更多定義葱弟,我們需要發(fā)布者對(duì)外提供一個(gè)API接口而不是在實(shí)現(xiàn)Observer接口的類中區(qū)分不同的邏輯壹店。

Delegates.observable()中提供了代表委托屬性三個(gè)參數(shù):元數(shù)據(jù)property: KProperty<*>對(duì)象,新舊值芝加。

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

    //
    fun onNormalPrice(price: Price)

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

    // 預(yù)購價(jià)
    fun onAdvancePrice(price: Price)

    // 會(huì)員價(jià)
    fun onVIPPrice(price: Price)
    //
}

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

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

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

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

}

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

// 被觀察者
class PriceSubject {
    // 觀察者對(duì)象
    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在新的值被賦值生效之前進(jìn)行截獲,然后根據(jù)相關(guān)業(yè)務(wù)邏輯處理它将塑。

   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)
            }
        }
        // 添加自定義規(guī)則
        if (newValue.type==3&&newValue.oldPrice < newValue.newPrice)
            true else throw IllegalArgumentException("會(huì)員價(jià)格不合理")

    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末脉顿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抬旺,更是在濱河造成了極大的恐慌弊予,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件开财,死亡現(xiàn)場(chǎng)離奇詭異汉柒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)责鳍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門碾褂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人历葛,你說我怎么就攤上這事正塌。” “怎么了恤溶?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵乓诽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我咒程,道長(zhǎng)鸠天,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任帐姻,我火速辦了婚禮稠集,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饥瓷。我一直安慰自己剥纷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布呢铆。 她就那樣靜靜地躺著晦鞋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棺克。 梳的紋絲不亂的頭發(fā)上鳖宾,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音逆航,去河邊找鬼。 笑死渔肩,一個(gè)胖子當(dāng)著我的面吹牛因俐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抹剩,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼撑帖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起澳眷,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤胡嘿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后钳踊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體衷敌,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年拓瞪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缴罗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡祭埂,死狀恐怖面氓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蛆橡,我是刑警寧澤舌界,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站泰演,受9級(jí)特大地震影響呻拌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粥血,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一柏锄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧复亏,春花似錦趾娃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至耕突,卻和暖如春笤成,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背眷茁。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工炕泳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人上祈。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓培遵,卻偏偏與公主長(zhǎng)得像浙芙,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子籽腕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容