觀察者模式, 發(fā)布-訂閱模式, 監(jiān)聽器模式
觀察者模式
觀察者模式是一種
行為型
設(shè)計(jì)模式, 定義對象間的一種一對多
的依賴關(guān)系,當(dāng)一個(gè)對象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對象都得到通知并被自動(dòng)更新
角色模型和結(jié)構(gòu)圖
在觀察者模式中,只有兩種主體:目標(biāo)對象 (Object
) 和 觀察者 (Observer
)。宗門任務(wù)大殿就是目標(biāo)對象,弟子們就是觀察者。
- Subject(主題): 主題是被觀察的對象,它維護(hù)了一個(gè)觀察者列表,并提供了添加、刪除和通知觀察者的方法
- Observer(觀察者): 觀察者是訂閱主題對象的對象,當(dāng)主題對象的狀態(tài)發(fā)生變化時(shí),觀察者會接收到通知并進(jìn)行相應(yīng)的處理。
結(jié)構(gòu)圖如下
代碼實(shí)現(xiàn)
觀察者抽象接口-Observer
/**
* @author whitebrocade
* @version 1.0
* @description: 抽象觀察者接口
*/
public interface Observer {
/**
* 發(fā)生改變時(shí)發(fā)送的message
* @param message 發(fā)送的message
*/
void update(Object message);
}
主題-Subject
/**
* @author whitebrocade
* @version 1.0
* @description: 主題
*/
public interface Subject {
/**
* 主題新增觀察者
* @param observer 要注冊的Observer
*/
void registerObserver(Observer observer);
/**
* 移除主題下的觀察者
* @param observer 要移除的Observer
*/
void removeObserver(Observer observer);
/**
* 通知該主題所有的Observer
* @param message 通知內(nèi)容
*/
void notifyObservers(Object message);
}
Observer實(shí)現(xiàn)類-User
/**
* @author whitebrocade
* @version 1.0
* @description: 微信公眾號的具體觀察者即用戶User
*/
public class User implements Observer {
/**
* 用戶姓名
*/
private final String name;
/**
* 接受的消息
*/
private Object message;
public User(String name) {
this.name = name;
}
/**
* 確認(rèn)消息
*/
public void read() {
System.out.println(name + "收到推送消息: " + message);
}
/**
* @param message 發(fā)送的message
*/
@Override
public void update(Object message) {
this.message = message;
read();
}
}
####Subject實(shí)現(xiàn)類- WechatServer
import java.util.ArrayList;
import java.util.List;
/**
* @author whitebrocade
* @version 1.0
* @description: 微信公共號
*/
public class WechatServer implements Subject {
/**
* 存儲Observer的列表
*/
private final List<Observer> observerList;
/**
* 推送的消息
*/
private Object message;
public WechatServer() {
observerList = new ArrayList<>();
}
/**
* @param observer 要注冊的Observer
*/
@Override
public void registerObserver(Observer observer) {
// 將Observer添加到列表中
observerList.add(observer);
}
/**
* @param observer 要移除的Observer
*/
@Override
public void removeObserver(Observer observer) {
// 移除observer
if (!observerList.isEmpty()) {
observerList.remove(observer);
}
}
/**
* @param message 通知內(nèi)容
*/
@Override
public void notifyObservers(Object message) {
// 遍歷被觀察者列表,通知每一個(gè)Observer
for (Observer observer : observerList) {
// 調(diào)用update進(jìn)行通知
observer.update(message);
}
}
/**
* 發(fā)送微信公眾號要推送的消息
* @param message 要發(fā)送的消息
*/
public void setInformation(Object message) {
this.message = message;
System.out.println("微信服務(wù)更新消息: " + message);
// 消息更新,通知所有觀察者
notifyObservers(message);
}
}
測試類-ObserverModeTest
/**
* @author whitebrocade
* @version 1.0
* @description: Observer測試
*/
public class ObserverModeTest {
public static void main(String[] args) {
WechatServer server = new WechatServer();
Observer jack = new User("Jack");
Observer smith = new User("Smith");
Observer kerry = new User("Kerry");
server.registerObserver(jack);
server.registerObserver(smith);
server.registerObserver(kerry);
server.setInformation("蟻鉗是蟻鉗, 蟹仔是蟹仔!");
System.out.println("----------------------------------------------");
// 將jack從觀察者集合中移除
server.removeObserver(jack);
server.setInformation("菜就多練, 練就不菜");
}
}
測試結(jié)果如下
發(fā)布-訂閱模式
大概很多人都和我一樣,覺得發(fā)布訂閱模式里的Publisher,就是觀察者模式里的Subject,而Subscriber,就是Observer。Publisher變化時(shí),就主動(dòng)去通知Subscriber。
其實(shí)并不是。
在發(fā)布訂閱模式里,發(fā)布者,并不會直接通知訂閱者,換句話說,發(fā)布者和訂閱者,彼此互不相識。之間交流通過Broker進(jìn)行
角色模型和結(jié)構(gòu)圖
- 發(fā)布者(Publisher):負(fù)責(zé)發(fā)布事件或消息到事件總線(Event Bus)中,讓訂閱者(Subscribers)可以接收到這些事件或消息。
-
EventBus(事件總線):作為發(fā)布者和訂閱者之間的中介者,負(fù)責(zé)接收發(fā)布者發(fā)布的事件,并將事件分發(fā)給所有訂閱者。事件總線可以是一個(gè)獨(dú)立的組件或者一個(gè)消息隊(duì)列
- 這里的發(fā)布者(事件總線)為一體
- Subscriber(訂閱者): 訂閱者訂閱感興趣的消息或事件,并從消息代理中接收相關(guān)的消息或事件
-
Event(事件): 事件是發(fā)布者發(fā)布的消息或事件,訂閱者可以根據(jù)自己的需求選擇訂閱特定的事件
代碼實(shí)現(xiàn)
Event
import lombok.Data;
/**
* @author whitebrocade
* @version 1.0
* @description: 抽象事件源
*/
@Data
public abstract class Event {
/**
* 事件名
*/
private String name;
/**
* 事件信息
*/
private Object message;
/**
* 事件信息
*/
private Object type;
}
Subscriber
/**
* @author whitebrocade
* @version 1.0
* @description: 定義訂閱者接口
*/
public interface Subscriber {
/**
* 事件觸發(fā)后執(zhí)行的邏輯
* @param event 事件
*/
void handleEvent(Event event);
}
MyEvent
/**
* @author whitebrocade
* @version 1.0
* @description: 自定義事件源
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MyEvent extends Event {
/**
* 觸發(fā)時(shí)間
*/
private Date triggerTime;
}
MySubscriber
/**
* @author whitebrocade
* @version 1.0
* @description: 定義具體訂閱者
*/
public class MySubscriber implements Subscriber {
/**
* 訂閱者名稱
*/
private final String name;
public MySubscriber(String name) {
this.name = name;
}
@Override
public void handleEvent(Event event) {
System.out.println(name + "訂閱的事件: " + event.toString());
}
}
EventBus
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author whitebrocade
* @version 1.0
* @description: 事件總線
*/
public class EventBus {
/**
* k-v存儲訂閱者名稱和訂閱者
*/
private Map<String, List<Subscriber>> subscriberList = new HashMap<>();
/**
* 訂閱事件
* @param eventName 事件名
* @param subscriber 訂閱者
*/
public void subscribe(String eventName, Subscriber subscriber) {
if (! subscriberList.containsKey(eventName)) {
subscriberList.put(eventName, new ArrayList<>());
}
subscriberList.get(eventName).add(subscriber);
}
/**
* 發(fā)布事件
* @param event 事件
*/
public void publish(Event event) {
List<Subscriber> eventSubscribers = subscriberList.get(event.getName());
if (eventSubscribers != null) {
for (Subscriber subscriber : eventSubscribers) {
subscriber.handleEvent(event);
}
}
}
}
測試類
import java.util.Date;
/**
* @author whitebrocade
* @version 1.0
* @description: 發(fā)布-訂閱模式測試類
*/
public class PublisherTest {
public static void main(String[] args) {
// 創(chuàng)建事件總線
EventBus eventBus = new EventBus();
// 創(chuàng)建訂閱者
Subscriber jack = new MySubscriber("Jack");
Subscriber tom = new MySubscriber("Tom");
// 訂閱事件
eventBus.subscribe("event1", jack);
eventBus.subscribe("event2", tom);
// 發(fā)布事件
MyEvent event1 = new MyEvent();
event1.setName("event1");
event1.setTriggerTime(new Date());
event1.setMessage("蟻鉗是蟻鉗, 蟹仔是蟹仔!");
event1.setType("消息一");
MyEvent event2 = new MyEvent();
event2.setName("event2");
event2.setTriggerTime(new Date());
event2.setMessage("菜就多練, 練就不菜!");
event2.setType("消息二");
eventBus.publish(event1);
eventBus.publish(event2);
}
}
測試結(jié)果如下
觀察者模式 和 發(fā)布-訂閱模式的對比
共同點(diǎn)
- 解耦性: 兩種模式都能實(shí)現(xiàn)發(fā)布者與訂閱者(觀察者)之間的解耦,使得發(fā)布者和訂閱者可以獨(dú)立地進(jìn)行擴(kuò)展和修改,互不影響。
-
事件通知: 在兩種模式中,發(fā)布者(主題)
發(fā)生變化
時(shí)會通知訂閱者(觀察者),訂閱者(觀察者)會相應(yīng)地處理這些事件或通知
區(qū)別
-
通信機(jī)制:
-
觀察者模式: 觀察者模式是
一對多
的通信機(jī)制,一個(gè)主題對象可以有多個(gè)觀察者對象訂閱它,當(dāng)主題對象狀態(tài)發(fā)生變化時(shí),所有訂閱者都會收到通知 -
發(fā)布-訂閱模式: 發(fā)布-訂閱模式是通過
一個(gè)消息代理(發(fā)布者)
來進(jìn)行通信,發(fā)布者將消息發(fā)送到消息代理,然后由消息代理將消息分發(fā)給所有訂閱者。訂閱者只需訂閱感興趣的事件,而不需要直接與發(fā)布者交互
-
觀察者模式: 觀察者模式是
-
關(guān)系建立:
-
觀察者模式: 在觀察者模式中,觀察者需要直接訂閱主題對象,主題對象需要維護(hù)一個(gè)觀察者
列表
- 發(fā)布-訂閱模式: 在發(fā)布-訂閱模式中,發(fā)布者和訂閱者之間通過一個(gè)消息代理(或事件總線Eventg)進(jìn)行通信,發(fā)布者和訂閱者之間不直接建立聯(lián)系
-
觀察者模式: 在觀察者模式中,觀察者需要直接訂閱主題對象,主題對象需要維護(hù)一個(gè)觀察者
-
靈活性:
-
觀察者模式: 觀察者模式在訂閱關(guān)系上是
靜態(tài)
的,即訂閱者需要直接訂閱特定的主題對象 -
發(fā)布-訂閱模式: 發(fā)布-訂閱模式在訂閱關(guān)系上是
動(dòng)態(tài)
的,訂閱者可以根據(jù)需要訂閱不同的事件或消息
-
觀察者模式: 觀察者模式在訂閱關(guān)系上是
監(jiān)聽器模式
監(jiān)聽器模式并不是一個(gè)新的設(shè)計(jì)模式,它是觀察者模式在特定場景下的一種改造和應(yīng)用
。通常,觀察者模式的主題在通知觀察者時(shí),通知中不包含任何信息。如果這個(gè)過程中攜帶了一些其他信息,那么主題本身就成為了事件源,而攜帶信息的封裝類就成為了事件。此時(shí)的觀察者模式,也就升級為監(jiān)聽器了。監(jiān)聽器模式是觀察者模式的另一種形態(tài)
角色模型和結(jié)構(gòu)圖
監(jiān)聽器模式通常包含三個(gè)角色:事件源、事件對象、事件監(jiān)聽器
- 事件源: 被監(jiān)聽的事件本身, 也就是可以觸發(fā)監(jiān)聽器的某件事件
- 事件對象: :事件對象里面存放了對事件源的引用, 可以通過事件對象來獲取事件源, 是對事件源的
包裝
- 事件監(jiān)聽器: 定義事件發(fā)生后的
動(dòng)作
代碼實(shí)現(xiàn)
Event
import lombok.Data;
/**
* @author whitebrocade
* @version 1.0
* @description: 抽象事件源
*/
@Data
public abstract class Event {
/**
* 事件信息
*/
private Object message;
/**
* 事件信息
*/
private Object type;
}
EventListener
/**
* @author whitebrocade
* @version 1.0
* @description: 監(jiān)聽器接口
*/
public interface EventListener {
/**
* 事件觸發(fā)時(shí)回調(diào)
* @param event 事件
*/
void onEventReceived(Event event);
}
EventSource
/**
* @author whitebrocade
* @version 1.0
* @description: 事件源類, 用于注冊監(jiān)聽器、觸發(fā)事件并通知所有監(jiān)聽器
*/
public interface EventSource {
/**
* 注冊事件監(jiān)聽器
* @param listener 監(jiān)聽器
*/
void addListener(EventListener listener);
/**
* 移除監(jiān)聽器
* @param listener 監(jiān)聽器
*/
void removeListener(EventListener listener);
/**
* 事件分發(fā)器, 將事件傳遞給所有注冊的事件監(jiān)聽器
* @param event 事件
*/
void fireEvent(Event event);
}
MyEvent
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.Date;
/**
* @author whitebrocade
* @version 1.0
* @description: 自定義事件源
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MyEvent extends Event {
/**
* 觸發(fā)時(shí)間
*/
private Date triggerTime;
}
MyListener
/**
* @author whitebrocade
* @version 1.0
* @description: 事件監(jiān)聽器
*/
public class MyListener implements EventListener {
@Override
public void onEventReceived(Event event) {
System.out.println("收到的事件: " + event.toString());
}
}
MyEventSource
import java.util.ArrayList;
import java.util.List;
/**
* @author whitebrocade
* @version 1.0
* @description: 事件源類, 用于注冊監(jiān)聽器、觸發(fā)事件并通知所有監(jiān)聽器
*/
public class MyEventSource implements EventSource {
/**
* 事件源集合
*/
private List<EventListener> listenerList = new ArrayList<>();
/**
* 注冊事件監(jiān)聽器
* @param listener 監(jiān)聽器
*/
public void addListener(EventListener listener) {
listenerList.add(listener);
}
/**
* 移除監(jiān)聽器
* @param listener 監(jiān)聽器
*/
public void removeListener(EventListener listener) {
listenerList.remove(listener);
}
/**
* 事件分發(fā)器, 將事件傳遞給所有注冊的事件監(jiān)聽器
* @param event 事件
*/
public void fireEvent(Event event) {
for (EventListener listener : listenerList) {
listener.onEventReceived(event);
}
}
}
ListenerTest
import java.util.Date;
/**
* @author whitebrocade
* @version 1.0
* @date 2024/2/21 21:24
* @description: TODO
*/
public class ListenerTest {
public static void main(String[] args) {
// 創(chuàng)建一個(gè)事件源
EventSource eventSource = new MyEventSource();
// 創(chuàng)建兩個(gè)事件監(jiān)聽器
MyListener listener1 = new MyListener();
MyListener listener2 = new MyListener();
// 將這兩個(gè)事件監(jiān)聽器注冊到事件源
eventSource.addListener(listener1);
eventSource.addListener(listener2);
// 創(chuàng)建一個(gè)事件event
MyEvent event = new MyEvent();
event.setTriggerTime(new Date());
event.setMessage("蟻鉗是蟻鉗, 蟹仔是蟹仔!");
event.setType("消息一");
// 將事件傳遞給事件源, 進(jìn)行分發(fā)
eventSource.fireEvent(event);
// 從事件源中移除一個(gè)listener2事件監(jiān)聽器
eventSource.removeListener(listener2);
// 再次創(chuàng)建一個(gè)事件event2
MyEvent event2 = new MyEvent();
event2.setTriggerTime(new Date());
event2.setMessage("菜就多練, 練就不菜!");
event2.setType("消息二");
// 將事件傳遞給事件源, 進(jìn)行分發(fā)
eventSource.fireEvent(event2);
}
}
測試結(jié)果如下
參考資料
觀察者模式 vs 發(fā)布訂閱模式,千萬不要再混淆了
觀察者模式 | 菜鳥教程
23 種設(shè)計(jì)模式詳解(全23種)-觀察者模式
觀察者 - 廖雪峰的官方網(wǎng)站
監(jiān)聽器模式和觀察者模式的關(guān)系,寫點(diǎn)你不知道的
【設(shè)計(jì)模式】-11監(jiān)聽者模式
【設(shè)計(jì)模式】-監(jiān)聽者模式和觀察者模式的區(qū)別與聯(lián)系_觀察者模式和監(jiān)聽者模式的區(qū)別
觀察者模式 vs 發(fā)布訂閱模式文章來源:http://www.zghlxwxcb.cn/news/detail-836342.html
[設(shè)計(jì)模式(四) —— 觀察者模式/發(fā)布訂閱模式](https://blog.csdn.net/weixin_37620587/article/details/130170062#:~:text=1.什么是發(fā)布-訂閱模式 1 發(fā)布-訂閱模式是一種行為設(shè)計(jì)模式,它允許多個(gè)對象通過事件的發(fā)布和訂閱來進(jìn)行通信; 2,在這種模式中,發(fā)布者 (又稱為主題)負(fù)責(zé)發(fā)布事件,而訂閱者 (也稱為觀察者)則通過訂閱主題來接收這些事件; 3 這種模式使得應(yīng)用程序的不同部分能夠松散耦合,并且可以動(dòng)態(tài)地添加或刪除訂閱者;)文章來源地址http://www.zghlxwxcb.cn/news/detail-836342.html
到了這里,關(guān)于觀察者模式, 發(fā)布-訂閱模式, 監(jiān)聽器模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!