作者已經(jīng)搬遷去隔壁網(wǎng)站,也歡迎大家關注我們的寫作團隊:天星技術團隊。
安利
我如何零基礎轉行成為一個自信的前端
雖然我只是個做app的,里面很多東西看了沒多大用,但我主要學習的是別人的習慣。我現(xiàn)在空閑時間算比較多的,平時想學一些東西的時候,卻總是被(自己)打擾。后來就用了里面提到的番茄時間,只需要自己克制一下不在規(guī)定時間內(nèi)看別的東西,學習起來還是蠻有效率的。里面其他的東西,你們也可以看看。
初識觀察者模式
我還能怎樣啊,就是學習了一下觀察者模式,再看了點源碼,再去學習rxjava1.0,再看2.0,這樣一套流程下來,看的是行云流水,思路不卡殼。
觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態(tài)時,它的所有依賴者都會收到通知并且自動更新。
我們會把多個依賴者稱為觀察者(訂閱者/Observer),一個被依賴者稱為主題(目標/Subject)

訂閱報紙,這個例子我不講,太多人講了,沒意思。
要玩就玩?zhèn)€騷的,比如我不講。

出版者 + 訂閱者 = 觀察者模式
這樣一句話相信大家就比較明白了,無論是講過無數(shù)次的訂閱報紙問題,還是訂閱什么東西,只要是當對象改變狀態(tài)需通知訂閱者知道的模式,就叫做觀察者模式(印象中也被叫做觀察者模式)。根本不用管每個訂閱者是否要更新還是啥都不做,只要通知到位了,就行。
手擼觀察者模式
要手擼代碼,我們還是先理清一下思緒,看看要做什么。
- 有一個Subject,還有一個Observer
- Subject需要存儲所有訂閱了的Observer,所以Subject有一個Observer集合,和添加/刪除方法
- Subject需要通知所有訂閱的Observer更新數(shù)據(jù),所以所有Observer都有一個可被Subject調(diào)用的數(shù)據(jù)更新的方法
- 沒有了,已經(jīng)滿足甲方的需求了。開擼吧!
橋豆麻袋?。。。。。。。。。?!
既然所有Observer都有至少一個更新方法的話,我們就提煉出一個父類出來。而Subject那邊,我們也提一個吧。項目里可能不止一個需要被監(jiān)控的目標。
我可真是個小機靈鬼兒?。。。?/strong>
一張圖說明上面的內(nèi)容。

開擼!?。。?/p>
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)//有這一個方法就夠了
}
在具體主題中,我們需要有一個狀態(tài)改變,來導致通知所有訂閱者更新的方法。
class ConcreteSubject : Subject {
private var subjectState: String? = null
fun getState(): String? {
return subjectState
}
fun ChangeState(subjectState : String){
this.subjectState = subjectState
notifyObservers()
}
}
具體觀察者中,同步一下代表收到消息就好了。在實際開發(fā)中在update()里面做自己想做的事就好了。
class ConcreteObserver : Observer {
private var observerState: String? = null
override fun update(subject: Subject) {
observerState = (subject as ConcreteSubject).getState()
}
}
在客戶端調(diào)用的時候,先注冊,再執(zhí)行changeState(),就能把當前已經(jīng)注冊的對象的observerState值改變。
“推”與“拉”
觀察者模式根據(jù)推送消息時的不同,又分為“推模型”和“拉模式”。
-
拉模式:
在上面寫的demo中,我們在Subject類中更新數(shù)據(jù)時,observer.update(this)。
這個this傳遞的就是concreteSubject實體對象。因為concreteObserver獲得了concreteSubject對象,所以需要什么信息時,直接從對象中拉去數(shù)據(jù)就行了。
這種主題對象在通知觀察者時,直接傳含有信息的對象的模式,叫做拉模型
//拉模型
observer.update(this)
-
推模式:
拉模型傳遞的一個對象,里面包含了許多信息,而推模型,就是將對象里面的具體信息,傳遞進去。這種主題對象在通知觀察者時,傳遞觀察者需要的具體信息的模式,叫做推模型。
//推模型
observer.update((this as ConcreteSubject).getState())
注意,用推模型的時候,接口參數(shù)跟拉模型是不一樣的。
-
安全性
拉模式是比較安全的方式,因為只會給訂閱者提供規(guī)定的信息。
而推模型相對來說會比較不安全,因為觀察者獲取的是一個包含了許多信息的對象,但是它可能不需要這么多信息,那多余的信息,就是一個安全隱患。但這樣的情況發(fā)生時,我建議是提供一些get方法,方便獲取不同的數(shù)據(jù)。
Java.util.Observer & Java.util.Observable
Java API有內(nèi)置的觀察者模式,為什么我們還要自己寫呢?
問得好! 稍后就講!
Java.util.Observer 和 Java.util.Observable 分別對應著我們寫的Observer 和 Subject。

這里update里面的兩個變量,第一個變量是主題本身,目的是為了讓觀察者知道是哪一個主題通知它的。第二個變量是傳入notifyObservser()的數(shù)據(jù)對象。

誒~
- 我們剛剛是把Subject寫成interface,這里是寫的class。
- 我們用的ArrayList存儲Observer,它用的是Vector來存儲。
- 在添加/刪除Observer上,它是寫的addObserver(),deleteObserver()。
- 它有notifyObservers() 和 notifyObservers(Object arg),來調(diào)用Observer的update方法。
- 多了一個布爾值屬性changed
現(xiàn)在來解決剛剛提的問題,為什么我們要自己寫個觀察者模式呢?
因為Java.util.Observable這玩意兒特么的不是接口,是個類啊!
如果我們要設計一個類想同時擁有Observable和另一個超類的行為的話,就根本沒辦法做,誰叫java不支持多重繼承呢。這點限制了Observable的復用能力。而復用正是我們使用設計模式的動機!
關于change
先來看關于change的幾個方法。



嗯~
懂了。這三個方法就更改/獲取change的值而已。在哪里用到了呢?
整個類中,只有notifyObservers(Object arg)中調(diào)用了這樣一段代碼。

哦!?。。。。。。。?!明白了。
也就是說,如果調(diào)用notifyObservers()前沒有調(diào)用setChanged(),觀察者就不會被通知。
這樣有啥子好處呢?
能靈活處理通知與否!數(shù)據(jù)可能每秒都在更新,但是觀察者卻不需要更新這么頻繁,就可以每隔一段時間,再調(diào)用一下setChange(),獲取一次數(shù)據(jù)。
反向思考一波,也就是說,在使用內(nèi)置的Observable時,在想讓觀察者被通知之前,一定要先執(zhí)行setChange()!
還有一些東西
- 在定義中,我們說到觀察者模式是一對多的依賴。一是主題,多是觀察者,依賴是觀察者對主題的單向依賴。觀察者不能主動獲取主題的信息,只能等主題通知更新。
- 主題通知觀察者更新的時候,順序是不確定的。 多個觀察者之間收到信息的順序是不確定的,別在此處想做什么騷操作。
- 當一個訂閱者訂閱了多個主題時,為了區(qū)別是哪個主題發(fā)來的消息,一般有兩種處理方式。1. 訂閱者擁有多個更新方法,每個主題調(diào)用不同的更新方法。2. update()接收到消息后先判斷是哪個主題發(fā)來的消息(參考Java.util.Observer)。
- 觀察者模式的優(yōu)點:實現(xiàn)了觀察者和目標之間的抽象耦合; 實現(xiàn)了動態(tài)聯(lián)動;
- 觀察者模式的缺點:由于主題通知的是所有注冊過的訂閱者,萬一某一條數(shù)據(jù)某一個訂閱者不需要更新,可能會引起數(shù)據(jù)的誤更新,就麻煩了。
最后
寫這篇文章本意是學習一下觀察者模式,但始終想不出來比較好(sao)的題目,于是才做了一次標題黨,也不知道UC會不會叫我去上班。
下個月開始,我不會再一直寫設計模式相關的東西。公司拖欠工資,即便是年底,我也要學點其他東西,準備一下面試。但是如果有人喜歡我這種畫風的文章和rxjava相關的東西的話,我會把《為了學習Rxjava,年輕小伙竟作出這種事!(2)》寫出來。
以下是我“設計模式系列”文章,歡迎大家關注留言投幣丟香蕉。
也可以進群跟大神們討論。qq群:557247785
設計模式入門
Java與Kotlin的單例模式
Kotlin的裝飾者模式與源碼擴展
由淺到深了解工廠模式
為了學習Rxjava,年輕小伙竟作出這種事!