观察者模式, 发布-订阅模式, 监听器模式
观察者模式
观察者模式是一种
行为型
设计模式, 定义对象间的一种一对多
的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
角色模型和结构图
在观察者模式中,只有两种主体:目标对象 (Object
) 和 观察者 (Observer
)。宗门任务大殿就是目标对象,弟子们就是观察者。
- Subject(主题): 主题是被观察的对象,它维护了一个观察者列表,并提供了添加、删除和通知观察者的方法
- Observer(观察者): 观察者是订阅主题对象的对象,当主题对象的状态发生变化时,观察者会接收到通知并进行相应的处理。
结构图如下
代码实现
观察者抽象接口-Observer
java
/**
* @author whitebrocade
* @version 1.0
* @description: 抽象观察者接口
*/
public interface Observer {
/**
* 发生改变时发送的message
* @param message 发送的message
*/
void update(Object message);
}
主题-Subject
java
/**
* @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 通知内容
*/
void notifyObservers(Object message);
}
Observer实现类-User
java
/**
* @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;
}
/**
* 确认消息
*/
public void read() {
System.out.println(name + "收到推送消息: " + message);
}
/**
* @param message 发送的message
*/
@Override
public void update(Object message) {
this.message = message;
read();
}
}
####Subject实现类- WechatServer
java
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 通知内容
*/
@Override
public void notifyObservers(Object message) {
// 遍历被观察者列表,通知每一个Observer
for (Observer observer : observerList) {
// 调用update进行通知
observer.update(message);
}
}
/**
* 发送微信公众号要推送的消息
* @param message 要发送的消息
*/
public void setInformation(Object message) {
this.message = message;
System.out.println("微信服务更新消息: " + message);
// 消息更新,通知所有观察者
notifyObservers(message);
}
}
测试类-ObserverModeTest
java
/**
* @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("菜就多练, 练就不菜");
}
}
测试结果如下
发布-订阅模式
大概很多人都和我一样,觉得发布订阅模式里的Publisher,就是观察者模式里的Subject,而Subscriber,就是Observer。Publisher变化时,就主动去通知Subscriber。
其实并不是。
在发布订阅模式里,发布者,并不会直接通知订阅者,换句话说,发布者和订阅者,彼此互不相识。之间交流通过Broker进行
角色模型和结构图
- 发布者(Publisher):负责发布事件或消息到事件总线(Event Bus)中,让订阅者(Subscribers)可以接收到这些事件或消息。
- EventBus(事件总线) :作为发布者和订阅者之间的中介者,负责接收发布者发布的事件,并将事件分发给所有订阅者。事件总线可以是一个独立的组件或者一个消息队列
- 这里的发布者(事件总线)为一体
- Subscriber(订阅者): 订阅者订阅感兴趣的消息或事件,并从消息代理中接收相关的消息或事件
- Event(事件): 事件是发布者发布的消息或事件,订阅者可以根据自己的需求选择订阅特定的事件
代码实现
Event
java
import lombok.Data;
/**
* @author whitebrocade
* @version 1.0
* @description: 抽象事件源
*/
@Data
public abstract class Event {
/**
* 事件名
*/
private String name;
/**
* 事件信息
*/
private Object message;
/**
* 事件信息
*/
private Object type;
}
Subscriber
java
/**
* @author whitebrocade
* @version 1.0
* @description: 定义订阅者接口
*/
public interface Subscriber {
/**
* 事件触发后执行的逻辑
* @param event 事件
*/
void handleEvent(Event event);
}
MyEvent
java
/**
* @author whitebrocade
* @version 1.0
* @description: 自定义事件源
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MyEvent extends Event {
/**
* 触发时间
*/
private Date triggerTime;
}
MySubscriber
java
/**
* @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
java
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);
}
/**
* 发布事件
* @param event 事件
*/
public void publish(Event event) {
List<Subscriber> eventSubscribers = subscriberList.get(event.getName());
if (eventSubscribers != null) {
for (Subscriber subscriber : eventSubscribers) {
subscriber.handleEvent(event);
}
}
}
}
测试类
java
import java.util.Date;
/**
* @author whitebrocade
* @version 1.0
* @description: 发布-订阅模式测试类
*/
public class PublisherTest {
public static void main(String[] args) {
// 创建事件总线
EventBus eventBus = new EventBus();
// 创建订阅者
Subscriber jack = new MySubscriber("Jack");
Subscriber tom = new MySubscriber("Tom");
// 订阅事件
eventBus.subscribe("event1", jack);
eventBus.subscribe("event2", tom);
// 发布事件
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);
}
}
测试结果如下
观察者模式 和 发布-订阅模式的对比
共同点
- 解耦性: 两种模式都能实现发布者与订阅者(观察者)之间的解耦,使得发布者和订阅者可以独立地进行扩展和修改,互不影响。
- 事件通知: 在两种模式中,发布者(主题)
发生变化
时会通知订阅者(观察者),订阅者(观察者)会相应地处理这些事件或通知
区别
- 通信机制:
- 观察者模式: 观察者模式是
一对多
的通信机制,一个主题对象可以有多个观察者对象订阅它,当主题对象状态发生变化时,所有订阅者都会收到通知 - 发布-订阅模式: 发布-订阅模式是通过
一个消息代理(发布者)
来进行通信,发布者将消息发送到消息代理,然后由消息代理将消息分发给所有订阅者。订阅者只需订阅感兴趣的事件,而不需要直接与发布者交互
- 观察者模式: 观察者模式是
- 关系建立:
- 观察者模式: 在观察者模式中,观察者需要直接订阅主题对象,主题对象需要维护一个观察者
列表
- 发布-订阅模式: 在发布-订阅模式中,发布者和订阅者之间通过一个消息代理(或事件总线Eventg)进行通信,发布者和订阅者之间不直接建立联系
- 观察者模式: 在观察者模式中,观察者需要直接订阅主题对象,主题对象需要维护一个观察者
- 灵活性:
- 观察者模式: 观察者模式在订阅关系上是
静态
的,即订阅者需要直接订阅特定的主题对象 - 发布-订阅模式: 发布-订阅模式在订阅关系上是
动态
的,订阅者可以根据需要订阅不同的事件或消息
- 观察者模式: 观察者模式在订阅关系上是
监听器模式
监听器模式并不是一个新的设计模式,它是观察者模式在特定场景下的一种改造和应用
。通常,观察者模式的主题在通知观察者时,通知中不包含任何信息。如果这个过程中携带了一些其他信息,那么主题本身就成为了事件源,而携带信息的封装类就成为了事件。此时的观察者模式,也就升级为监听器了。监听器模式是观察者模式的另一种形态
角色模型和结构图
监听器模式通常包含三个角色:事件源、事件对象、事件监听器
- 事件源: 被监听的事件本身, 也就是可以触发监听器的某件事件
- 事件对象: :事件对象里面存放了对事件源的引用, 可以通过事件对象来获取事件源, 是对事件源的
包装
- 事件监听器: 定义事件发生后的
动作
代码实现
Event
java
import lombok.Data;
/**
* @author whitebrocade
* @version 1.0
* @description: 抽象事件源
*/
@Data
public abstract class Event {
/**
* 事件信息
*/
private Object message;
/**
* 事件信息
*/
private Object type;
}
EventListener
java
/**
* @author whitebrocade
* @version 1.0
* @description: 监听器接口
*/
public interface EventListener {
/**
* 事件触发时回调
* @param event 事件
*/
void onEventReceived(Event event);
}
EventSource
java
/**
* @author whitebrocade
* @version 1.0
* @description: 事件源类, 用于注册监听器、触发事件并通知所有监听器
*/
public interface EventSource {
/**
* 注册事件监听器
* @param listener 监听器
*/
void addListener(EventListener listener);
/**
* 移除监听器
* @param listener 监听器
*/
void removeListener(EventListener listener);
/**
* 事件分发器, 将事件传递给所有注册的事件监听器
* @param event 事件
*/
void fireEvent(Event event);
}
MyEvent
java
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 {
/**
* 触发时间
*/
private Date triggerTime;
}
MyListener
java
/**
* @author whitebrocade
* @version 1.0
* @description: 事件监听器
*/
public class MyListener implements EventListener {
@Override
public void onEventReceived(Event event) {
System.out.println("收到的事件: " + event.toString());
}
}
MyEventSource
java
import java.util.ArrayList;
import java.util.List;
/**
* @author whitebrocade
* @version 1.0
* @description: 事件源类, 用于注册监听器、触发事件并通知所有监听器
*/
public class MyEventSource implements EventSource {
/**
* 事件源集合
*/
private List<EventListener> listenerList = new ArrayList<>();
/**
* 注册事件监听器
* @param listener 监听器
*/
public void addListener(EventListener listener) {
listenerList.add(listener);
}
/**
* 移除监听器
* @param listener 监听器
*/
public void removeListener(EventListener listener) {
listenerList.remove(listener);
}
/**
* 事件分发器, 将事件传递给所有注册的事件监听器
* @param event 事件
*/
public void fireEvent(Event event) {
for (EventListener listener : listenerList) {
listener.onEventReceived(event);
}
}
}
ListenerTest
java
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) {
// 创建一个事件源
EventSource eventSource = new MyEventSource();
// 创建两个事件监听器
MyListener listener1 = new MyListener();
MyListener listener2 = new MyListener();
// 将这两个事件监听器注册到事件源
eventSource.addListener(listener1);
eventSource.addListener(listener2);
// 创建一个事件event
MyEvent event = new MyEvent();
event.setTriggerTime(new Date());
event.setMessage("蚁钳是蚁钳, 蟹仔是蟹仔!");
event.setType("消息一");
// 将事件传递给事件源, 进行分发
eventSource.fireEvent(event);
// 从事件源中移除一个listener2事件监听器
eventSource.removeListener(listener2);
// 再次创建一个事件event2
MyEvent event2 = new MyEvent();
event2.setTriggerTime(new Date());
event2.setMessage("菜就多练, 练就不菜!");
event2.setType("消息二");
// 将事件传递给事件源, 进行分发
eventSource.fireEvent(event2);
}
}
测试结果如下
参考资料
【设计模式】-监听者模式和观察者模式的区别与联系_观察者模式和监听者模式的区别
[设计模式(四) ------ 观察者模式/发布订阅模式](https://blog.csdn.net/weixin_37620587/article/details/130170062#:\~:text=1.什么是发布-订阅模式 1 发布-订阅模式是一种行为设计模式,它允许多个对象通过事件的发布和订阅来进行通信; 2,在这种模式中,发布者 (又称为主题)负责发布事件,而订阅者 (也称为观察者)则通过订阅主题来接收这些事件; 3 这种模式使得应用程序的不同部分能够松散耦合,并且可以动态地添加或删除订阅者;)