实习周记(十五):通知接口的设计
对于通知接口的优化
在项目中,由禁言功能衍生出多场景多类型的通知需求,面对这种情况,我们采用“发布-订阅者”模式构建一个EventBus来进行优化
观察者模式和发布订阅者模式
存在部分人认为发布者 + 订阅者 = 观察者,尽管两者较为相似,但是存在一个比较明显的区别
区别
- 发布订阅者模式不会直接将消息发送给订阅者,而是通过broker进行一个中转分发
- 发布者/订阅者模式多数为异步方式(使用消息队列),观察者则大多为同步方式实现
观察者模式(Observer Design Pattern)
在对象间定义一个一对多的依赖,当一个对象发生改变时,所有依赖的对象都会自动收到通知
优点
- 解耦
- 建立了一套触发通知机制
缺点
- 被观察者有很多直接或者间接的观察者,通知时间会很长
- 观察者与被观察者间存在循环依赖的话,观察目标时将会循环调用,直至系统崩溃
- 仅仅知道观察目标发生了变化,却不知道为什么发生变法
示例
import java.util.ArrayList;
import java.util.List;
/**
* 主题(被观察者)
*/
public class Subject {
private List<Observer> observers = new ArrayList<Observer>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer){
observers.add(observer);
}
public void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
/**
* 观察者
*/
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
/**
* 实体观察类(订阅目标)
*/
public class OctalObserver extends Observer{
public OctalObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Octal String: "
+ Integer.toOctalString( subject.getState() ) );
}
}
/**
* demo
*/
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new OctalObserver(subject);
System.out.println("First state change: 15");
subject.setState(10);
System.out.println("Second state change: 10");
subject.setState(5);
}
}
输出结果:
测试对象: 1
Octal String: 12
测试对象: 2
Octal String: 5
示例代码中,
- subject可以定义条件来调用observer中的方法,以此达到不同的观察效果(示例中只定义了一个方法,暂无体现此效果)
- observer可以定义为抽象类或者接口,通过继承或者实现它的类来完成不同的功能定制
- 观察者模式中每个被订阅的目标都需要有对观察者的处理
- 在demo中,调用顺序为:Subject --> Observer --> OctalObserver
发布订阅者模式
发布者和订阅者之前不会知道双方的存在,通过第三方来进行内容的中转分发
优缺点与观察者类似
调度中心存在的意义在于对代码进行再一次解耦
示例代码
@Getter
@ToString
public class Event<T> {
protected T data;
private long eventTime = System.currentTimeMillis();
public Event(T data) {
this.data = data;
}
}
@Slf4j
public class EventBus {
public static EventPublisher publisher = new DefaultEventPublisher();
public static void register(EventPublisher publisher) {
EventBus.publisher = publisher;
}
public static void publish(Event event) {
publisher.publish(event);
}
public static void asyncPublish(Event event) {
publisher.asyncPublish(event);
}
public static class DefaultEventPublisher implements EventPublisher {
@Override
public void publish(Event event) {
log.info("default event publish event:{}", event);
}
@Override
public void asyncPublish(Event event) {
log.info("default event asyncPublish event:{}", event);
}
}
}
public interface EventPublisher {
/**
* 发送事件
*
* @param event
*/
void publish(Event event);
/**
* 异步发送事件
*
* @param event
*/
void asyncPublish(Event event);
}
@Component
@AllArgsConstructor
@Slf4j
public class SpringEventPublisher implements EventPublisher {
private final ApplicationEventPublisher eventPublisher;
@Override
public void publish(Event event) {
log.debug("发送事件:{}", event);
eventPublisher.publishEvent(event);
}
@Override
@Async
public void asyncPublish(Event event) {
log.debug("发送异步事件:{}", event);
eventPublisher.publishEvent(event);
}
}
@Component
@AllArgsConstructor
public class PublisherRegister implements InitializingBean {
private final EventPublisher eventPublisher;
@Override
public void afterPropertiesSet() throws Exception {
EventBus.register(eventPublisher);
}
}
总结
- 可以将发布/订阅者模式当成多了一个调度中心的观察者模式
- 使用这两种设计模式的原因都在于解耦,避免因为场景发生变更而产生大量的修改成本
- 扩张性强