实习周记(十五):通知接口的设计

对于通知接口的优化

在项目中,由禁言功能衍生出多场景多类型的通知需求,面对这种情况,我们采用“发布-订阅者”模式构建一个EventBus来进行优化

观察者模式和发布订阅者模式

存在部分人认为发布者 + 订阅者 = 观察者,尽管两者较为相似,但是存在一个比较明显的区别

区别

  1. 发布订阅者模式不会直接将消息发送给订阅者,而是通过broker进行一个中转分发
  2. 发布者/订阅者模式多数为异步方式(使用消息队列),观察者则大多为同步方式实现

观察者模式(Observer Design Pattern)

在对象间定义一个一对多的依赖,当一个对象发生改变时,所有依赖的对象都会自动收到通知

优点

  1. 解耦
  2. 建立了一套触发通知机制

缺点

  1. 被观察者有很多直接或者间接的观察者,通知时间会很长
  2. 观察者与被观察者间存在循环依赖的话,观察目标时将会循环调用,直至系统崩溃
  3. 仅仅知道观察目标发生了变化,却不知道为什么发生变法

示例

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


示例代码中,

  1. subject可以定义条件来调用observer中的方法,以此达到不同的观察效果(示例中只定义了一个方法,暂无体现此效果)
  2. observer可以定义为抽象类或者接口,通过继承或者实现它的类来完成不同的功能定制
  3. 观察者模式中每个被订阅的目标都需要有对观察者的处理
  4. 在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);
    }
}

总结

  1. 可以将发布/订阅者模式当成多了一个调度中心的观察者模式
  2. 使用这两种设计模式的原因都在于解耦,避免因为场景发生变更而产生大量的修改成本
  3. 扩张性强
posted @ 2021-12-02 17:17  2月2日  阅读(143)  评论(0)    收藏  举报