為了學習Rxjava,年輕小夥竟作出這種事!

點先生發表於2019-02-26

作者 點先生 日期 2018.10.26

安利

我如何零基礎轉行成為一個自信的前端
雖然我只是個做app的,裡面很多東西看了沒多大用,但我主要學習的是別人的習慣。我現在空閒時間算比較多的,平時想學一些東西的時候,卻總是被(自己)打擾。後來就用了裡面提到的番茄時間,只需要自己剋制一下不在規定時間內看別的東西,學習起來還是蠻有效率的。裡面其他的東西,你們也可以看看。

初識觀察者模式

我還能怎樣啊,就是學習了一下觀察者模式,再看了點原始碼,再去學習rxjava1.0再看2.0,這樣一套流程下來,看的是行雲流水,思路不卡殼。

觀察者模式定義了物件之間的一對多依賴,這樣一來,當一個物件改變狀態時,它的所有依賴者都會收到通知並且自動更新。

我們會把多個依賴者稱為觀察者(訂閱者/Observer),一個被依賴者稱為主題(目標/Subject)

為了學習Rxjava,年輕小夥竟作出這種事!

訂閱報紙,這個例子我不講,太多人講了,沒意思。
要玩就玩個騷的,比如……
我不講。

為了學習Rxjava,年輕小夥竟作出這種事!

出版者 + 訂閱者 = 觀察者模式

這樣一句話相信大家就比較明白了,無論是講過無數次的訂閱報紙問題,還是訂閱什麼東西,只要是當物件改變狀態需通知訂閱者知道的模式,就叫做觀察者模式(印象中也被叫做觀察者模式)。根本不用管每個訂閱者是否要更新還是啥都不做,只要通知到位了,就行。

手擼觀察者模式

要手擼程式碼,我們還是先理清一下思緒,看看要做什麼。

  1. 有一個Subject,還有一個Observer
  2. Subject需要儲存所有訂閱了的Observer,所以Subject有一個Observer集合,和新增/刪除方法
  3. Subject需要通知所有訂閱的Observer更新資料,所以所有Observer都有一個可被Subject呼叫的資料更新的方法
  4. 沒有了,已經滿足甲方的需求了。開擼吧!

橋豆麻袋!!!!!!!!!!!

既然所有Observer都有至少一個更新方法的話,我們就提煉出一個父類出來。而Subject那邊,我們也提一個吧。專案裡可能不止一個需要被監控的目標。
我可真是個小機靈鬼兒!!!!
一張圖說明上面的內容。

為了學習Rxjava,年輕小夥竟作出這種事!

開擼!!!!

interface Subject {
    var observers: ArrayList<Observer>
        get() = ArrayList<Observer>()
        set(value) = TODO()

    fun registerObserver(observer: Observer) {//註冊觀察者
        observers.add(observer)
    }
    fun removeObserver(observer: Observer) {//刪除觀察者
        observers.remove(observer)
    }
    fun notifyObservers() {//更新
        for (observer in observers) {
            observer.update(this)//通知觀察者更新
        }
    }
}
複製程式碼
interface Observer {
    fun update(subject: Subject)//有這一個方法就夠了
}
複製程式碼

在具體主題中,我們需要有一個狀態改變,來導致通知所有訂閱者更新的方法。

class ConcreteSubject : Subject {
    private var subjectState: String? = null

    fun getState(): String? { 
        return subjectState
    }
    fun ChangeState(subjectState : String){
        this.subjectState = subjectState
        notifyObservers()
    }
}
複製程式碼

具體觀察者中,同步一下代表收到訊息就好了。在實際開發中在update()裡面做自己想做的事就好了。


class ConcreteObserver : Observer {
    private var observerState: String? = null

    override fun update(subject: Subject) {
        observerState = (subject as ConcreteSubject).getState()
    }
}
複製程式碼

在客戶端呼叫的時候,先註冊,再執行changeState(),就能把當前已經註冊的物件的observerState值改變。

“推”與“拉”

觀察者模式根據推送訊息時的不同,又分為**“推模型”“拉模式”。**

  1. 拉模式
    在上面寫的demo中,我們在Subject類中更新資料時,observer.update(this)。
    這個this傳遞的就是concreteSubject實體物件。因為concreteObserver獲得了concreteSubject物件,所以需要什麼資訊時,直接從物件中拉去資料就行了。
    這種主題物件在通知觀察者時,直接傳含有資訊的物件的模式,叫做拉模型
//拉模型
observer.update(this)
複製程式碼
  1. 推模式
    拉模型傳遞的一個物件,裡面包含了許多資訊,而推模型,就是將物件裡面的具體資訊,傳遞進去。
    這種主題物件在通知觀察者時,傳遞觀察者需要的具體資訊的模式,叫做推模型。
//推模型
observer.update((this as ConcreteSubject).getState())
複製程式碼

注意,用推模型的時候,介面引數跟拉模型是不一樣的。

  1. 安全性:
    拉模式是比較安全的方式,因為只會給訂閱者提供規定的資訊。
    而推模型相對來說會比較不安全,因為觀察者獲取的是一個包含了許多資訊的物件,但是它可能不需要這麼多資訊,那多餘的資訊,就是一個安全隱患。但這樣的情況發生時,我建議是提供一些get方法,方便獲取不同的資料。

Java.util.Observer & Java.util.Observable

Java API有內建的觀察者模式,為什麼我們還要自己寫呢?
問得好! 稍後就講!
Java.util.Observer 和 Java.util.Observable 分別對應著我們寫的Observer 和 Subject。

Java.util.Observer

這裡update裡面的兩個變數,第一個變數是主題本身,目的是為了讓觀察者知道是哪一個主題通知它的。第二個變數是傳入notifyObservser()的資料物件。

Java.util.Observable

誒~

  1. 我們剛剛是把Subject寫成interface,這裡是寫的class。
  2. 我們用的ArrayList儲存Observer,它用的是Vector來儲存。
  3. 在新增/刪除Observer上,它是寫的addObserver(),deleteObserver()。
  4. 它有notifyObservers() 和 notifyObservers(Object arg),來呼叫Observer的update方法。
  5. 多了一個布林值屬性changed
  • 現在來解決剛剛提的問題,為什麼我們要自己寫個觀察者模式呢?
    因為Java.util.Observable這玩意兒特麼的不是介面,是個類啊! 如果我們要設計一個類想同時擁有Observable和另一個超類的行為的話,就根本沒辦法做,誰叫java不支援多重繼承呢。這點限制了Observable的複用能力。而複用正是我們使用設計模式的動機!

  • 關於change
    先來看關於change的幾個方法。

為了學習Rxjava,年輕小夥竟作出這種事!

為了學習Rxjava,年輕小夥竟作出這種事!

為了學習Rxjava,年輕小夥竟作出這種事!

嗯~
懂了。這三個方法就更改/獲取change的值而已。在哪裡用到了呢?
整個類中,只有notifyObservers(Object arg)中呼叫了這樣一段程式碼。

為了學習Rxjava,年輕小夥竟作出這種事!

哦!!!!!!!!!!明白了。
也就是說,如果呼叫notifyObservers()前沒有呼叫setChanged(),觀察者就不會被通知。 這樣有啥子好處呢?
能靈活處理通知與否!資料可能每秒都在更新,但是觀察者卻不需要更新這麼頻繁,就可以每隔一段時間,再呼叫一下setChange(),獲取一次資料。

反向思考一波,也就是說,在使用內建的Observable時,在想讓觀察者被通知之前,一定要先執行setChange()!

還有一些東西

  • 在定義中,我們說到觀察者模式是一對多的依賴。一是主題,多是觀察者,依賴是觀察者對主題的單向依賴。觀察者不能主動獲取主題的資訊,只能等主題通知更新。
  • 主題通知觀察者更新的時候,順序是不確定的。 多個觀察者之間收到資訊的順序是不確定的,別在此處想做什麼騷操作。
  • 當一個訂閱者訂閱了多個主題時,為了區別是哪個主題發來的訊息,一般有兩種處理方式。1. 訂閱者擁有多個更新方法,每個主題呼叫不同的更新方法。2. update()接收到訊息後先判斷是哪個主題發來的訊息(參考Java.util.Observer)。
  • 觀察者模式的優點:實現了觀察者和目標之間的抽象耦合; 實現了動態聯動;
  • 觀察者模式的缺點:由於主題通知的是所有註冊過的訂閱者,萬一某一條資料某一個訂閱者不需要更新,可能會引起資料的誤更新,就麻煩了。

最後

寫這篇文章本意是學習一下觀察者模式,但始終想不出來比較好(sao)的題目,於是才做了一次標題黨,也不知道UC會不會叫我去上班。
下個月開始,我不會再一直寫設計模式相關的東西。公司拖欠工資,即便是年底,我也要學點其他東西,準備一下面試。但是如果有人喜歡我這種畫風的文章和rxjava相關的東西的話,我會把《為了學習Rxjava,年輕小夥竟作出這種事!(2)》寫出來

以下是我“設計模式系列”文章,歡迎大家關注留言投幣丟香蕉。
也可以進群跟大神們討論。qq群:557247785

設計模式入門
Java與Kotlin的單例模式
Kotlin的裝飾者模式與原始碼擴充套件
由淺到深瞭解工廠模式
為了學習Rxjava,年輕小夥竟作出這種事!

相關文章