观察者
这在项目中多处使用。例如:授权手机号成功领取可赠送的会员卡,购物签到送积分等等。
定义
顾名思义,观察者模式(Observer Pattern)定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。
也可以理解为事件
什么时候使用它
一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
优缺点
优点:
- 观察者和被观察者是抽象耦合的。
- 建立了一套触发机制。
缺点:
- 一个被观察者对象有很多的直接和间接观察者的话,将所有的观察者都通知到会花费很多时间。
核心实现
- 主题(Subject)
也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
- 观察者(Observer)
观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
- 具体主题(Concrete Subject)
具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。
- 具体观察者(Concrete Observer)
具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。
示例
需求是,签到送积分。
先来定义一个主题(Subject):
java
/**
* 主题
*
* @author mjyang
* @date 2023/5/23 17:03
*/
public class Subject {
private List<Observer> observers = new ArrayList<Observer>();
public void attach(Observer observer) {
observers.add(observer);
}
public void signin() {
// 执行签到逻辑
...
notifyAllObservers();
}
public void notifyAllObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
再来定义观察者(Observer):
java
/**
* 抽象观察者
*
* @author mjyang
* @date 2023/5/23 17:03
*/
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
再来定义具体观察者(Concrete Observer):
java
/**
* 签到观察者
*
* @author mjyang
* @date 2023/5/23 17:03
*/
public class SigninObserver extends Observer {
public SigninObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("用户xxx签到,送x积分");
}
}
接下来就是客户端调用了:
java
public static void main(String[] args) {
Subject subject = new Subject();
SigninObserver signinObserver = new SigninObserver(subject);
subject.signin();
}
通过这个简单的示例,演示了如何定义一个观察者模式。
最佳实践
spring event 便是经典的观察者模式实现。我们可以很容易的去使用它。标品里大量在使用。
在分布式环境中,更理想的方案为 mq。单机环境下可以使用 spring event。
我们仍然可以通过接口来统一实现。例如标品提供的抽象实现如下:
java
/**
* 消息发送服务
*
* @author mjyang
*/
public interface MqSendService {
/**
* 事件发送
*
* @param event event
*/
void publish(Object event);
}
java
/**
* spring event 事件发送处理实现类
*
* @author mjyang
* @date 2021-08-27 17:07:32
*/
@Service
@Slf4j
@ConditionalOnProperty(prefix = "wmeimob.eventbus", name = "storage", havingValue = "local")
@RequiredArgsConstructor
public class EventBusLocalSendServiceImpl implements MqSendService {
private final ApplicationEventPublisher applicationEventPublisher;
@Override
public void publish(Object event) {
if (log.isDebugEnabled()) {
log.debug("消息发送,{}", event);
}
try {
this.applicationEventPublisher.publishEvent(event);
} catch (Exception e) {
log.error("消息发送失败,{}", event);
}
}
}
java
/**
* mq 事件发送处理实现类
*
* @author mjyang
* @date 2021-08-27 17:07:32
*/
@Service
@Slf4j
@ConditionalOnProperty(prefix = "wmeimob.eventbus", name = "storage", havingValue = "mq")
@RequiredArgsConstructor
public class EventBusMQSendServiceImpl implements MqSendService {
private final XXXMQ applicationEventPublisher;
@Override
public void publish(Object event) {
if (log.isDebugEnabled()) {
log.debug("消息发送,{}", event);
}
try {
this.applicationEventPublisher.xxx(event);
} catch (Exception e) {
log.error("消息发送失败,{}", event);
}
}
}