定義
定義對(duì)象間的一種一對(duì)多的依賴關(guān)系趁矾,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。
觀察者模式的UML類圖及說明
如上圖(圖片來源于《head_first設(shè)計(jì)模式》)所示,觀察者的uml中主要有以下類
1.主題Subject(接口)
Subject 對(duì)象帶有綁定觀察者到 Client 對(duì)象和從 Client 對(duì)象解綁觀察者的方法,主要為觀察者注冊(cè)方法(registerObserver)账蓉,觀察者刪除方法(removeObserver),通知觀察者方法(notifyObservers)
2.觀察者Observer(接口)
Observer觀察者一般是一個(gè)接口逾一,每一個(gè)實(shí)現(xiàn)該接口的實(shí)現(xiàn)類都是具體觀察者沥割。主要提供一個(gè)update方法嫩痰。
3.具體主題ConcreteSubject(Subject的具體實(shí)現(xiàn))
是對(duì)主題Subject接口的具體實(shí)現(xiàn)猜欺,該類可根據(jù)具體業(yè)務(wù)擴(kuò)展
4.具體觀察者ConcreteObserver(Observer的具體實(shí)現(xiàn))
是對(duì)Observer觀察者的具體實(shí)現(xiàn)颖低,一般訂閱者會(huì)有多個(gè),所以該類可通過自己需要的消息進(jìn)行擴(kuò)展
觀察者模式的優(yōu)缺點(diǎn)及應(yīng)用場(chǎng)景
優(yōu)點(diǎn):
1.觀察者和被觀察者是抽象耦合的陌宿。
2.建立一套觸發(fā)機(jī)制锡足。
缺點(diǎn):
1.如果一個(gè)被觀察者對(duì)象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間壳坪。
2.如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話舶得,觀察目標(biāo)會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰爽蝴。
3.觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對(duì)象是怎么發(fā)生變化的沐批,而僅僅只是知道觀察目標(biāo)發(fā)生了變化纫骑。
應(yīng)用場(chǎng)景:
1.關(guān)聯(lián)行為場(chǎng)景
2.事件多級(jí)觸發(fā)場(chǎng)景
4.跨系統(tǒng)的消息變換場(chǎng)景,如消息隊(duì)列的處理機(jī)制
應(yīng)用實(shí)例:
1.新聞的發(fā)布訂閱
2.天氣信息的發(fā)布訂閱
注意事項(xiàng):
1九孩、JAVA 中已經(jīng)有了對(duì)觀察者模式的支持類先馆。
2、避免循環(huán)引用躺彬。
3煤墙、如果順序執(zhí)行,某一觀察者錯(cuò)誤會(huì)導(dǎo)致系統(tǒng)卡殼宪拥,一般采用異步方式仿野。
Spring中的觀察者模式
1.事件(ApplicationEvent)
ApplicationEvent 是所有事件對(duì)象的父類。ApplicationEvent 繼承自 jdk 的 EventObject, 所有的事件都需要繼承 ApplicationEvent, 并且通過source得到事件源江解。
2.事件監(jiān)聽(ApplicationListener)
ApplicationListener 事件監(jiān)聽器设预,也就是觀察者。繼承自 jdk 的 EventListener犁河,該類中只有一個(gè)方法 onApplicationEvent。當(dāng)監(jiān)聽的事件發(fā)生后該方法會(huì)被執(zhí)行魄梯。
3.事件發(fā)布(ApplicationContext)
ApplicationContext 是 Spring 中的核心容器桨螺,在事件監(jiān)聽中 ApplicationContext 可以作為事件的發(fā)布者,也就是事件源酿秸。因?yàn)?ApplicationContext 繼承自 ApplicationEventPublisher灭翔。在 ApplicationEventPublisher 中定義了事件發(fā)布的方法 — publishEvent(Object event)
4.事件管理(ApplicationEventMulticaster)
ApplicationEventMulticaster 用于事件監(jiān)聽器的注冊(cè)和事件的廣播。監(jiān)聽器的注冊(cè)就是通過它來實(shí)現(xiàn)的辣苏,它的作用是把 Applicationcontext 發(fā)布的 Event 廣播給它的監(jiān)聽器列表肝箱。
觀察者模式的實(shí)現(xiàn)
現(xiàn)在的線上直播模式非常的火,在這里稀蟋,以簡單的LOL比賽信息訂閱為例煌张。比如我們要關(guān)注今天有哪些了LPL的比賽信息,后幾天有哪些比賽信息退客,我們就可以點(diǎn)擊訂閱骏融,訂閱后,有新的比賽信息萌狂,就會(huì)推送給你档玻。
這樣的場(chǎng)景下,我們就可以用觀察者模式來實(shí)現(xiàn):
定義主題接口
/**
* 主題接口,對(duì)象通過此接口注冊(cè)為觀察者茫藏,或者把自己從觀察者中刪除
*
* @author yyl
*/
public interface Subject {
/**
* 觀察者注冊(cè)
*
*/
void registerObserver(Observer observer);
/**
* 刪除觀察者
*
*/
void removeObserver(Observer observer);
/**
* 通知觀察者
*
*/
void notifyObservers();
}
定義主題接口的具體實(shí)現(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务傲,觀察者一般是一個(gè)接口凉当,每一個(gè)實(shí)現(xiàn)該接口的實(shí)現(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;
// 觀察者注冊(cè)
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;
// 觀察者注冊(cè)
this.subject.registerObserver(this);
}
@Override
public void update() {
this.msgList = this.subject.getMsgList();
System.out.println("比賽預(yù)告:");
msgList.forEach(s -> System.out.println(s));
}
}
通知者進(jìn)行通知 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);
}
}
如上代碼,有兩個(gè)觀察者泊窘,在發(fā)布信息的比賽信息后熄驼,觀察者就可以獲取自己關(guān)注的信息,然后進(jìn)行自己的業(yè)務(wù)邏輯處理烘豹。代碼輸出如下