最近想深入研究下響應式程式設計,作為基礎很有必要來把觀察者模式擼一遍;一開始我是覺得很easy,然後就直接開擼了,擼著擼著發現擼不動了。因為我突然不太明白這個模式了,說好的觀察者,到底釋出-訂閱的兩者執行者誰才是觀察者?又或者說還有其他角色?但是根據《JAVA與模式》一書中的結構,並沒有額外的角色出現。
思考中....,好吧想不出來....,跑步去...
跑步時我給自己羅列了幾個問題:
這裡先丟擲定義:GOF給觀察者模式如下定義:定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新。
- 既然是物件狀態發生變更,那麼到底是誰的狀態發生了變更,又導致了誰被通知。
- 觀察者模式既然又可以稱之為“釋出-訂閱模式”,那麼對應起來,觀察者到底承當了“釋出”的角色還是“訂閱”的角色。就是說觀察者到底是主動的還是被動的?
- 被觀察者又幹了什麼事?它是主動的還是被動的角色?
這裡由於一些定式思維,總會覺得既然是“被觀察者”,那麼這個“被”字就是不是就表明“被觀察者”是被動接受變更的一方,也就是接受通知的一方呢?
之前我也是走到這個衚衕裡了,程式寫完總覺得哪裡不對;回過頭看,還是自己太年輕,沒有get到哪些大佬們的點。
先來看程式;這裡用掘金來打個比方,我的部落格glmmaper作為被觀察者,也就是釋出者。掘金小夥伴們作為觀察者,也就是訂閱者。
具體邏輯:小夥伴們(訂閱者)關注(訂閱)了我的部落格(釋出者),如果我釋出了一篇文章(狀態變更),就會通知(推送訊息)所有關注我的小夥伴。
package com.glmapper.designmode.observor;
/**
* @description: 抽象主題介面
* @email: <a href="glmapper_2018@163.com"></a>
* @author: 磊叔
* @date: 18/4/22
*/
public interface Subject {
/**
* 新增關注者
* @param observer 關注的小夥伴
*/
void addFocusObserver(Observer observer);
/**
* 取消關注
* @param observer 取消關注的小夥伴
*/
void removeFocusObserver(Observer observer);
/**
* 通知機制,通知機制由相關事件來觸發,比如說釋出文章
* @param blogName 部落格名
* @param articleName 文章名
*/
void notifyObservers(String blogName,String articleName);
}
複製程式碼
三個方法,一個是部落格主頁增加了一個關注者;一個是部落格主頁有小夥伴取消的關注(對於部落格來說就是移除一個關注者,這裡不知道是否也會覺得彆扭?明明你取消的關注,為啥說成是我移除你,也就是不讓你關注了,還能這麼玩?這裡肯定是需要在引入其他的一些輔助機制,比如說你在客戶端發起了一個取消關注的請求,後端處理的時候掘金的工程師們就是在我的關注列表中將你移除的,嗯,這麼一想確實是我不讓你關注了。?....);最後一個方法是發起一個通知。下面是一個具體的部落格,比如說是glmapper;
package com.glmapper.designmode.observor;
import java.util.ArrayList;
import java.util.List;
/**
* @description: 這個是具體釋出者,這裡比喻成我的部落格glmapper
* @email: <a href="glmapper_2018@163.com"></a>
* @author: 磊叔
* @date: 18/4/22
*/
public class ConcreteSubject implements Subject {
/** 我的當前關注列表 */
List<Observer> Observers = new ArrayList<>();
/** 我的部落格名 :求關注 */
private static final String blogName = "glmapper";
@Override
public void addFocusObserver(Observer observer) {
Observers.add(observer);
}
@Override
public void removeFocusObserver(Observer observer) {
Observers.remove(observer);
}
@Override
public void notifyObservers(String blogName,String articleName) {
for (Observer observer:Observers) {
observer.update(blogName,articleName);
}
}
/**
* 這裡是釋出文章,觸發通知事件
*/
public void publishArticle(String articleName){
notifyObservers(blogName,articleName);
}
}
複製程式碼
前面提到,通知事件肯定是由於某些狀態發生變更了,才會進行通知,這裡就可以比方為我釋出了一篇部落格,然後通知你(這裡只能假如你關注了)。再來看觀察者:
package com.glmapper.designmode.observor;
/**
* @description: 訂閱者抽象介面
* @email: <a href="glmapper_2018@163.com"></a>
* @author: 磊叔
* @date: 18/4/22
*/
public interface Observer {
/**
* 呼叫此方法會更新狀態,做出相應的動作
* @param blogName
* @param articleName
*/
void update(String blogName,String articleName);
}
複製程式碼
抽象訂閱者,有一個update方法,通知你去做出相應的動作,具體動作每個觀察者都可能不同。
package com.glmapper.designmode.observor;
/**
* @description: 這個是具體訂閱者,這裡可以比喻成部落格關注者,
* 收到變更資訊之後需要做出相應的動作
* @email: <a href="glmapper_2018@163.com"></a>
* @author: 磊叔
* @date: 18/4/22
*/
public class ConcreteObserver implements Observer {
@Override
public void update(String blogName,String articleName) {
System.out.println(blogName+"釋出了新的文章,文章名為:"+articleName);
read(articleName);
}
private void read(String articleName){
System.out.println("即將閱讀 "+articleName+" 這篇文章");
}
}
複製程式碼
上面是一個具體的關注者,加入說就是你。部落格更新之後發了一個通知給你(掘金app推送的訊息),然後你點了一下,這個也是一種動作。例子中舉的是read,就是關注者做出閱讀的動作。
看下最後的執行結果:
package com.glmapper.designmode.observor;
/**
* @description: [描述文字]
* @email: <a href="glmapper_2018@163.com"></a>
* @author: 磊叔
* @date: 18/4/22
*/
public class MyMainIndex{
public static void main(String[] args) {
//部落格主體
ConcreteSubject subject = new ConcreteSubject();
//關注者:handSome是帥氣的意思
Observer handSome = new ConcreteObserver();
//增加一個關注者
subject.addFocusObserver(handSome);
//發一篇文章
subject.publishArticle("設計模式-觀察者模式");
}
}
glmapper釋出了新的文章,文章名為:設計模式-觀察者模式
即將閱讀 設計模式-觀察者模式 這篇文章
複製程式碼
酒桶說:啊,歡樂時光總是短暫的
所以作為積累,還是需要將一些基本的概念來羅列一下的。
主要角色:
- 抽象主題角色(Subject:主題角色將所有對觀察者物件的引用儲存在一個集合中,每個主題可以有任意多個觀察者。抽象主題提供了增加和刪除等觀察者物件的介面。
- 抽象觀察者角色(Observer):為所有的具體觀察者定義一個介面,在觀察的主題發生改變時更新自己。
- 具體主題角色(ConcreteSubject)(1個):儲存相關狀態到具體觀察者物件,當具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色通常用一個具體子類實現。
- 具體觀察者角色(ConcretedObserver)(多個):儲存一個具體主題物件,儲存相關狀態,實現抽象觀察者角色所要求的更新介面,以使得其自身狀態和主題的狀態保持一致。
具體關係:
-
抽象主題(Subject)(介面)-->被具體主題(ConcreteSubject)角色(1個)實現
-
抽象觀察者(Observer)(介面)-->被具體觀察者(ConcretedObserver)角色(N個)實現
-
觀察者物件載入主題方法,並在主題方法中呼叫觀察者物件實現的介面方法update來讓自己發生變更響應。
一些場景:
- 當對一個物件的的改動會引發其他物件的變動時,而且你無法預測有多少個物件需要被改動。
- 當一個物件需要有能力通知其他物件,且不需要了解這些物件是什麼型別時。
基於釋出訂閱的具體實現例子還是很多的,比較典型的就是這種訂閱一個部落格,然後部落格更新推送;還有微信公眾號,服務號這些。
到這裡我們再回過頭來看一開始留下的幾個問題:
- 被觀察者的狀態發生變更,然後“主動通知”觀察者,並不是說,觀察者主動去獲取通知。
- 被觀察者是訊息釋出者,觀察者是訊息訂閱者;觀察者是被動接受者。
- 被觀察者的作用就是儲存當前的觀察者列表,然後提供一些通知機制來告訴觀察者自己發生了狀態變更,是主動者。
OK,觀察者模式就擼到這裡,也歡迎小夥伴們提出自己珍貴的意見;有寫的不當之處煩請及時提出。
播報:菜鳥成長系列又開始更新了....