前言
观察者是一种行为设计模式,它定义了的关系:当一个对象发生变化,依赖它的多个对象都会收到通知并自动更新。
猫咪可以晒着太阳睡觉,邻居在家看书,物业监控中心看着每一个进出的人。这些观察者都各自做着其他的事,不用一直盯着7号楼11层业主是否唱歌。被观察者一旦唱歌,这个观察者都会听到“执行”自己的update()。
观察者:只需要有一个对“感兴趣事件”的反应方式update();
被观察者:负责通知观察者执行它的update();管理观察者(ArrayList),注册,删除
被观察者
G2高铁到达南京站,两位到南京的乘客要下车。对于G2到达南京这件事,乘客Passenger1、Passenger2都很感兴趣,但它们不能在两个小时里啥也不做,就盯着是否到达南京。期间它们可以刷视频、看小说。到达南京站,列车会报站通知。乘客下车。 高铁有很多,所以我们需要把高铁共同点抽象出来成为subject:管理旅客列表(add,remove),报站(notify)
主题subject
public interface Subject_主题or被观察者or高铁_EventSource {
void addPassenger(Observer_or观察者or旅客or_Listener passenger);
void removePassenger(Observer_or观察者or旅客or_Listener passenger);
void notifyPassenger(ArriveStationEvent ae) throws InterruptedException;
}
具体被观察者-G2
到了具体被观察者,就需要一个管理观察者注册的列表
public class G2 implements Subject_主题or被观察者or高铁_EventSource{
public ArrayList<Observer_or观察者or旅客or_Listener> passengerActions = new ArrayList<>();
@Override
public void addPassenger(Observer_or观察者or旅客or_Listener passenger) {
passengerActions.add(passenger);
}
@Override
public void removePassenger(Observer_or观察者or旅客or_Listener passenger) {
passengerActions.remove(passenger);
}
@Override
public void notifyPassenger(ArriveStationEvent ae) throws InterruptedException {
Thread.sleep(2000);//模拟收拾行李下车
for(int i=0;i<passengerActions.size();i++){
passengerActions.get(i).getOff(ae);
}
}
}
Observer
观察者抽象出来对兴趣事件的反应方式(乘客上下车)
public interface Observer_or观察者or旅客or_Listener {
void getOn(ArriveStationEvent ae);
void getOff(ArriveStationEvent ae);
}
具体观察者
具体观察者实现自己不同的反应,一个拿行李,另一个拿手机电源
public class Passenger1 implements Observer_or观察者or旅客or_Listener{
@Override
public void getOn(ArriveStationEvent ae) {
System.out.println("我是Passenger1上车啦!站台信息:"+ae.getStartOffStation());
}
@Override
public void getOff(ArriveStationEvent ae) {
System.out.println("拿上行李,Passenger1下车啦!站台信息:"+ae.getArriveStation());
}
}
public class Passenger2 implements Observer_or观察者or旅客or_Listener{
@Override
public void getOn(ArriveStationEvent ae) {
System.out.println("我是Passenger2上车啦!车辆信息:"+ae.getStartOffStation());
}
@Override
public void getOff(ArriveStationEvent ae) {
System.out.println("带上电源线,我是Passenger2下车啦!车辆信息:"+ae.getArriveStation());
}
}
调用
加入线程,实现异步,模拟了,列车到站,通知完乘客,广播还可以播报广告。乘客下车和列车广播实现了真正的异步。
public class Station {
public String stationname = "南京";
public static void main(String[] args) {
G2 g2 = new G2();
g2.addPassenger(new Passenger1());
g2.addPassenger(new Passenger2());
//乘客在车上不用盯着高铁是否到达南京,可以玩手机,到达南京站,高铁会报站让乘客下车
ArriveStationEvent ae = new ArriveStationEvent();
ae.setArriveStation("南京");
new Thread(() -> {
try {
g2.notifyPassenger(ae);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
System.out.println("列车开始播报南京旅游广告:南京中山陵、秦淮河");
}
}
说明
下面就是一个数据载体,在事件驱动里的Event携带了事件发生时的主要数据内容。
public class ArriveStationEvent {
public String startOffStation;
public String arriveStation;
public String getStartOffStation() {
return startOffStation;
}
public void setStartOffStation(String startOffStation) {
this.startOffStation = startOffStation;
}
public String getArriveStation() {
return arriveStation;
}
public void setArriveStation(String arriveStation) {
this.arriveStation = arriveStation;
}
}
总结
无论是是观察者,被观察者,都有有一个父接口,这是面向对象的关键抽象,将共同行为抽取出来,新的参与者进来,对于这些共性方法调用的地方不用改变。我们观察者模式的关键在于,将观察者和状态监控解耦
状态监控耦合(主动轮询 vs 被动通知)
// 未解耦时的主动轮询(反模式)
while(true) {
if(train.getCurrentStation().equals("北京")) {
disembark(); // 需要持续检查状态
}
}
观察者模式通过将「状态监控」和「状态响应」分离
- 被观察者:专注状态管理
- 观察者:专注响应处理
无需观察者主动查询状态
graph LR
A[被观察者] -->|维护| B(观察者列表)
B --> C[Observer接口]
C --> D{具体观察者}
C --> E{其他观察者}
style A fill:#f9f,stroke:#333
style C fill:#b9f,stroke:#333
发布订阅
上面我们已经看到了,乘客有上车也有下车,如果一个乘客上车,一个乘客下车,怎么实现?维护两个观察者列表,上车下车?那不同站上下车了呢?列车维护每个站列表(上下车)?
graph LR
A[被观察者_G2高铁] -->|维护| B(观察者_南京下车乘客列表)
A[被观察者_G2高铁] -->|维护| J(观察者_南京上车乘客列表)
A[被观察者_G2高铁] -->|维护| H(观察者_济南下车乘客列表)
A[被观察者_G2高铁] -->|维护| G(观察者_济南上车乘客列表)
H --> C[Observer接口]
B --> C[Observer接口]
G --> C[Observer接口]
J --> C[Observer接口]
C --> D{具体观察者_济南西下车}
C --> I{具体观察者_济南西上车}
C --> E{其他观察者_南京下车}
C --> Q{其他观察者_南京上车}
style A fill:#f9f,stroke:#333
style C fill:#b9f,stroke:#333
观察者上下车,车站还需要列车到站信息组织检票,这都作为一个个列表交给G2维护?岂不是累死。所以把列表维护提取出来,彻底解耦观察者和被观察者之间的联系。那么这个观察者列表集就由消息队列broker来实现。broker来管理观察者的订阅,接收被观察者产生的消息,通知观察者。你订阅了南京站下车消息,列车到南京站,它只用发送一个南京站到达的消息给broker,broker分发这个消息给站务工作人员,南京站下车的乘客收到短信,站内等待上车的旅客也会收到短信。发布订阅核心就是将观察者列表的维护和通知提取出来交给中间件
graph LR
%% 观察者模式结构 %%
subgraph Observer[观察者模式]
A(Subject被观察者)--维护-->B[Observer List]
B-->C(ObserverA)
B-->D(ObserverB)
end
%% 发布-订阅模式结构 %%
subgraph PubSub[发布-订阅模式]
E(Publisher发布者)--发布事件-->F[[broker]]
F--路由事件-->G(SubscriberA)
F--路由事件-->H(SubscriberB)
F-.动态绑定.->I(New Subscriber)
end
style F fill:#6f9,stroke:#333