iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 21
1
Mobile Development

程式初學:Android與Kotlin系列 第 21

Day 21--天氣app(七)觀察者模式 Observer Pattern

  • 分享至 

  • xImage
  •  

第二種做法,在做之前要先了解一下
叫做觀察者模式

當多個 Class 都需要接收同一種資料的變化時,就適合使用 Observer Pattern
---參考自Design Pattern

這個練習的專案就像上面這句話

各顯示的fragment就是觀察者,同樣需要接收來自MainActivity查詢的資料變化以更新畫面
MainActivity(被觀察者,目標)提供訂閱,當有資料更新時發出通知
fragment(觀察者)可訂閱或退訂,來決定要不要接收通知

在改寫天氣的專案前,先試著做個觀察者模式的app練習理解看看

假設上方中間的爲(被觀察者),下方左右二個爲(觀察者)
當中間欄位輸入資料,按下更新
下方觀察者若有訂閱的話,也會跟著更新

常見的觀察者模式有包含觀察者與被觀察者的介面

interface

目標(被觀察者)的interface

建立一個包含3種方法的interface
分別是

  • 更新有訂閱的觀察者
  • 讓觀察者訂閱
  • 讓觀察者取消訂閱
interface IPublisher {
    fun getTitleChanged(title: String)
    fun add(subscriber: IObserver)
    fun remove(subscriber: IObserver)
}

觀察者的interface

建立一個可以收到通知的interface

  • 只須有一個接收傳入更新的參數,實作這個方法的觀察者都可以接收到目標的更新
interface IObserver {
    fun update(targetTitle: String)
}

目標class

建立被觀察者的class並實作interface IPublisher

  • 宣告一個可變的list subScribers,用於存放IObserver型態的觀察者清單
  • 覆寫getTitleChanged(),被呼叫時接收傳入值以改變可變變數title
  • 覆寫add(subscriber: IObserver),用於將訂閱者加入subScribers清單中
  • 覆寫remove(subscriber: IObserver),用於將訂閱者從subScribers清單中移除
class TargetObject : IPublisher {
    
    private val subScribers = mutableListOf<IObserver>()

    override fun getTitleChanged(title: String) {
        subScribers.forEach { it.update(title) }
    }

    override fun add(subscriber: IObserver) {
        subScribers.add(subscriber)
    }

    override fun remove(subscriber: IObserver) {
        subScribers.remove(subscriber)
    }
}

觀察者class

原本我建立了觀察者的class並實作interface IObserver

class Observer : IObserver{
    var observerTitle = "observer"

    override fun update(targetTitle: String) {
        observerTitle =targetTitle
    }

再到MainActivity實例化,並且每次target更新時,將實例化的屬性再給textview

val observerOne = Observer()
...
btn_update.setOnClickListener {
            target.getTitleChanged(ed_target.text.toString())
            tv_observer1.text = observerOne.observerTitle
                   }

請教導師後,得到建議的更好做法,以這個練習來說
可使用物件表示式,以匿名物件的方式直接實作interface的update

    private val observerOne = object : IObserver {
        override fun update(targetTitle: String) {
            tv_observer1.text = targetTitle
        }
    }

這樣不需要特別建立一個觀察者的class
並且在目標更新的時候,資料傳入觀察者的update方法就可以直接給textview顯示了

觀察者訂閱/取消訂閱

var checkScriberOne = false

btn_scriber1.setOnClickListener {
            when (checkScriberOne) {
                false -> {
                    target.add(observerOne)
                    btn_scriber1.text = "已訂閱"
                    checkScriberOne = true
                    btn_scriber1.setTextColor(Color.RED)    
                }
                true -> {
                    target.remove(observerOne)
                    btn_scriber1.text = "未訂閱"
                    checkScriberOne = false
                    btn_scriber1.setTextColor(Color.BLACK)
                }
            }
        }

執行的結果像是這樣


上一篇
Day 20--天氣app(六)類型檢測與類型轉換,在fragment建立更新的方法
下一篇
Day 22--天氣app(八)觀察者模式,匿名函數,lambda表示式
系列文
程式初學:Android與Kotlin30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言