安利
我如何零基礎轉行成為一個自信的前端
雖然我只是個做app的,裡面很多東西看了沒多大用,但我主要學習的是別人的習慣。我現在空閒時間算比較多的,平時想學一些東西的時候,卻總是被(自己)打擾。後來就用了裡面提到的番茄時間,只需要自己剋制一下不在規定時間內看別的東西,學習起來還是蠻有效率的。裡面其他的東西,你們也可以看看。
初識觀察者模式
我還能怎樣啊,就是學習了一下觀察者模式,再看了點原始碼,再去學習rxjava1.0,再看2.0,這樣一套流程下來,看的是行雲流水,思路不卡殼。
觀察者模式定義了物件之間的一對多依賴,這樣一來,當一個物件改變狀態時,它的所有依賴者都會收到通知並且自動更新。
我們會把多個依賴者稱為觀察者(訂閱者/Observer),一個被依賴者稱為主題(目標/Subject)
訂閱報紙,這個例子我不講,太多人講了,沒意思。
要玩就玩個騷的,比如……
我不講。
出版者 + 訂閱者 = 觀察者模式
這樣一句話相信大家就比較明白了,無論是講過無數次的訂閱報紙問題,還是訂閱什麼東西,只要是當物件改變狀態需通知訂閱者知道的模式,就叫做觀察者模式(印象中也被叫做觀察者模式)。根本不用管每個訂閱者是否要更新還是啥都不做,只要通知到位了,就行。
手擼觀察者模式
要手擼程式碼,我們還是先理清一下思緒,看看要做什麼。
- 有一個Subject,還有一個Observer
- Subject需要儲存所有訂閱了的Observer,所以Subject有一個Observer集合,和新增/刪除方法
- Subject需要通知所有訂閱的Observer更新資料,所以所有Observer都有一個可被Subject呼叫的資料更新的方法
- 沒有了,已經滿足甲方的需求了。開擼吧!
橋豆麻袋!!!!!!!!!!!
既然所有Observer都有至少一個更新方法的話,我們就提煉出一個父類出來。而Subject那邊,我們也提一個吧。專案裡可能不止一個需要被監控的目標。
我可真是個小機靈鬼兒!!!!
一張圖說明上面的內容。
開擼!!!!
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值改變。
“推”與“拉”
觀察者模式根據推送訊息時的不同,又分為**“推模型”和“拉模式”。**
- 拉模式:
在上面寫的demo中,我們在Subject類中更新資料時,observer.update(this)。
這個this傳遞的就是concreteSubject實體物件。因為concreteObserver獲得了concreteSubject物件,所以需要什麼資訊時,直接從物件中拉去資料就行了。
這種主題物件在通知觀察者時,直接傳含有資訊的物件的模式,叫做拉模型
//拉模型
observer.update(this)
複製程式碼
- 推模式:
拉模型傳遞的一個物件,裡面包含了許多資訊,而推模型,就是將物件裡面的具體資訊,傳遞進去。
這種主題物件在通知觀察者時,傳遞觀察者需要的具體資訊的模式,叫做推模型。
//推模型
observer.update((this as ConcreteSubject).getState())
複製程式碼
注意,用推模型的時候,介面引數跟拉模型是不一樣的。
- 安全性:
拉模式是比較安全的方式,因為只會給訂閱者提供規定的資訊。
而推模型相對來說會比較不安全,因為觀察者獲取的是一個包含了許多資訊的物件,但是它可能不需要這麼多資訊,那多餘的資訊,就是一個安全隱患。但這樣的情況發生時,我建議是提供一些get方法,方便獲取不同的資料。
Java.util.Observer & Java.util.Observable
Java API有內建的觀察者模式,為什麼我們還要自己寫呢?
問得好! 稍後就講!
Java.util.Observer 和 Java.util.Observable 分別對應著我們寫的Observer 和 Subject。
這裡update裡面的兩個變數,第一個變數是主題本身,目的是為了讓觀察者知道是哪一個主題通知它的。第二個變數是傳入notifyObservser()的資料物件。
誒~
- 我們剛剛是把Subject寫成interface,這裡是寫的class。
- 我們用的ArrayList儲存Observer,它用的是Vector來儲存。
- 在新增/刪除Observer上,它是寫的addObserver(),deleteObserver()。
- 它有notifyObservers() 和 notifyObservers(Object arg),來呼叫Observer的update方法。
- 多了一個布林值屬性changed
-
現在來解決剛剛提的問題,為什麼我們要自己寫個觀察者模式呢?
因為Java.util.Observable這玩意兒特麼的不是介面,是個類啊!
如果我們要設計一個類想同時擁有Observable和另一個超類的行為的話,就根本沒辦法做,誰叫java不支援多重繼承呢。這點限制了Observable的複用能力。而複用正是我們使用設計模式的動機! -
關於change
先來看關於change的幾個方法。
嗯~
懂了。這三個方法就更改/獲取change的值而已。在哪裡用到了呢?
整個類中,只有notifyObservers(Object arg)中呼叫了這樣一段程式碼。
哦!!!!!!!!!!明白了。
也就是說,如果呼叫notifyObservers()前沒有呼叫setChanged(),觀察者就不會被通知。
這樣有啥子好處呢?
能靈活處理通知與否!資料可能每秒都在更新,但是觀察者卻不需要更新這麼頻繁,就可以每隔一段時間,再呼叫一下setChange(),獲取一次資料。
反向思考一波,也就是說,在使用內建的Observable時,在想讓觀察者被通知之前,一定要先執行setChange()!
還有一些東西
- 在定義中,我們說到觀察者模式是一對多的依賴。一是主題,多是觀察者,依賴是觀察者對主題的單向依賴。觀察者不能主動獲取主題的資訊,只能等主題通知更新。
- 主題通知觀察者更新的時候,順序是不確定的。 多個觀察者之間收到資訊的順序是不確定的,別在此處想做什麼騷操作。
- 當一個訂閱者訂閱了多個主題時,為了區別是哪個主題發來的訊息,一般有兩種處理方式。1. 訂閱者擁有多個更新方法,每個主題呼叫不同的更新方法。2. update()接收到訊息後先判斷是哪個主題發來的訊息(參考Java.util.Observer)。
- 觀察者模式的優點:實現了觀察者和目標之間的抽象耦合; 實現了動態聯動;
- 觀察者模式的缺點:由於主題通知的是所有註冊過的訂閱者,萬一某一條資料某一個訂閱者不需要更新,可能會引起資料的誤更新,就麻煩了。
最後
寫這篇文章本意是學習一下觀察者模式,但始終想不出來比較好(sao)的題目,於是才做了一次標題黨,也不知道UC會不會叫我去上班。
下個月開始,我不會再一直寫設計模式相關的東西。公司拖欠工資,即便是年底,我也要學點其他東西,準備一下面試。但是如果有人喜歡我這種畫風的文章和rxjava相關的東西的話,我會把《為了學習Rxjava,年輕小夥竟作出這種事!(2)》寫出來。
以下是我“設計模式系列”文章,歡迎大家關注留言投幣丟香蕉。
也可以進群跟大神們討論。qq群:557247785
設計模式入門
Java與Kotlin的單例模式
Kotlin的裝飾者模式與原始碼擴充套件
由淺到深瞭解工廠模式
為了學習Rxjava,年輕小夥竟作出這種事!