定義
定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。
觀察者模式的UML類圖及說明

如上圖(圖片來源于《head_first設(shè)計模式》)所示,觀察者的uml中主要有以下類
1.主題Subject(接口)
Subject 對象帶有綁定觀察者到 Client 對象和從 Client 對象解綁觀察者的方法,主要為觀察者注冊方法(registerObserver),觀察者刪除方法(removeObserver),通知觀察者方法(notifyObservers)
2.觀察者Observer(接口)
Observer觀察者一般是一個接口,每一個實現(xiàn)該接口的實現(xiàn)類都是具體觀察者。主要提供一個update方法。
3.具體主題ConcreteSubject(Subject的具體實現(xiàn))
是對主題Subject接口的具體實現(xiàn),該類可根據(jù)具體業(yè)務(wù)擴展
4.具體觀察者ConcreteObserver(Observer的具體實現(xiàn))
是對Observer觀察者的具體實現(xiàn),一般訂閱者會有多個,所以該類可通過自己需要的消息進行擴展
觀察者模式的優(yōu)缺點及應(yīng)用場景
優(yōu)點:
1.觀察者和被觀察者是抽象耦合的。
2.建立一套觸發(fā)機制。
缺點:
1.如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
2.如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話,觀察目標(biāo)會觸發(fā)它們之間進行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。
3.觀察者模式?jīng)]有相應(yīng)的機制讓觀察者知道所觀察的目標(biāo)對象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。
應(yīng)用場景:
1.關(guān)聯(lián)行為場景
2.事件多級觸發(fā)場景
4.跨系統(tǒng)的消息變換場景,如消息隊列的處理機制
應(yīng)用實例:
1.新聞的發(fā)布訂閱
2.天氣信息的發(fā)布訂閱
注意事項:
1、JAVA 中已經(jīng)有了對觀察者模式的支持類。
2、避免循環(huán)引用。
3、如果順序執(zhí)行,某一觀察者錯誤會導(dǎo)致系統(tǒng)卡殼,一般采用異步方式。
Spring中的觀察者模式
1.事件(ApplicationEvent)
ApplicationEvent 是所有事件對象的父類。ApplicationEvent 繼承自 jdk 的 EventObject, 所有的事件都需要繼承 ApplicationEvent, 并且通過source得到事件源。
2.事件監(jiān)聽(ApplicationListener)
ApplicationListener 事件監(jiān)聽器,也就是觀察者。繼承自 jdk 的 EventListener,該類中只有一個方法 onApplicationEvent。當(dāng)監(jiān)聽的事件發(fā)生后該方法會被執(zhí)行。
3.事件發(fā)布(ApplicationContext)
ApplicationContext 是 Spring 中的核心容器,在事件監(jiān)聽中 ApplicationContext 可以作為事件的發(fā)布者,也就是事件源。因為 ApplicationContext 繼承自 ApplicationEventPublisher。在 ApplicationEventPublisher 中定義了事件發(fā)布的方法 — publishEvent(Object event)
4.事件管理(ApplicationEventMulticaster)
ApplicationEventMulticaster 用于事件監(jiān)聽器的注冊和事件的廣播。監(jiān)聽器的注冊就是通過它來實現(xiàn)的,它的作用是把 Applicationcontext 發(fā)布的 Event 廣播給它的監(jiān)聽器列表。
觀察者模式的實現(xiàn)
現(xiàn)在的線上直播模式非常的火,在這里,以簡單的LOL比賽信息訂閱為例。比如我們要關(guān)注今天有哪些了LPL的比賽信息,后幾天有哪些比賽信息,我們就可以點擊訂閱,訂閱后,有新的比賽信息,就會推送給你。
這樣的場景下,我們就可以用觀察者模式來實現(xiàn):
定義主題接口
/**
* 主題接口,對象通過此接口注冊為觀察者,或者把自己從觀察者中刪除
*
* @author yyl
*/
public interface Subject {
/**
* 觀察者注冊
*
*/
void registerObserver(Observer observer);
/**
* 刪除觀察者
*
*/
void removeObserver(Observer observer);
/**
* 通知觀察者
*
*/
void notifyObservers();
}
定義主題接口的具體實現(xiàn),及LOL比賽信息主題
/**
* lol主題
*
* @author yyl
*/
public class LolSubject implements Subject {
/** 今日比賽信息*/
private String msg;
/** 后幾日比賽信息*/
private List<String> msgList;
// 用戶列表
private static List<Observer> userList = new ArrayList<>();
@Override
public void registerObserver(Observer observer) {
userList.add(observer);
}
@Override
public void removeObserver(Observer observer) {
userList.remove(observer);
}
@Override
public void notifyObservers() {
userList.forEach(observer -> observer.update());
}
/**
* 發(fā)布信息,并通知觀察者
*
* @author yyl
*/
public void setMsgs(String msg,List<String> msgList){
// 更改通知信息
this.msg = msg;
this.msgList = msgList;
// 通知觀察者
notifyObservers();
}
public String getMsg() {
return msg;
}
public List<String> getMsgList() {
return msgList;
}
}
定義觀察者Observer
/**
* 觀察者Observer,觀察者一般是一個接口,每一個實現(xiàn)該接口的實現(xiàn)類都是具體觀察者
*
* @author yyl
*/
public interface Observer {
/**
* 接受消息后的具體邏輯處理
*/
void update();
}
具體觀察者,今日比賽信息觀察者
/**
* 具體觀察者,今日比賽信息觀察者
*
* @author yyl
*/
public class CurrentObserver implements Observer {
/**lol比賽信息主題 */
private LolSubject subject;
/**訂閱的信息 */
private String msg;
public CurrentObserver(LolSubject subject) {
this.subject = subject;
// 觀察者注冊
this.subject.registerObserver(this);
}
@Override
public void update() {
this.msg = this.subject.getMsg();
System.out.println("今日比賽:" + this.msg);
}
}
具體觀察者,后幾日比賽信息觀察者
/**
* 具體觀察者,后幾日比賽信息觀察者
*
* @author yyl
*/
public class FutureObserver implements Observer {
/**lol比賽信息主題 */
private LolSubject subject;
/**訂閱的信息 */
private List<String> msgList;
public FutureObserver(LolSubject subject) {
this.subject = subject;
// 觀察者注冊
this.subject.registerObserver(this);
}
@Override
public void update() {
this.msgList = this.subject.getMsgList();
System.out.println("比賽預(yù)告:");
msgList.forEach(s -> System.out.println(s));
}
}
通知者進行通知 Client
public class TestObserver {
public static void main(String[] args) {
// 主題
LolSubject lolSubject = new LolSubject();
// 觀察者
CurrentObserver currentObserver = new CurrentObserver(lolSubject);
FutureObserver futureObserver = new FutureObserver(lolSubject);
List<String> msgList = new ArrayList<>();
msgList.add("2020-06-22 RNG VS IG");
msgList.add("2020-06-22 VG VS LGD");
msgList.add("2020-06-22 V5 VS OMG");
// 發(fā)布信息
lolSubject.setMsgs("TSE VS FPX", msgList);
}
}
如上代碼,有兩個觀察者,在發(fā)布信息的比賽信息后,觀察者就可以獲取自己關(guān)注的信息,然后進行自己的業(yè)務(wù)邏輯處理。代碼輸出如下
