目录与学习目标

  1. 1:观察者模式 入门理解
  2. 2:经典代码流程
  3. 1:发布者 订阅者 抽象类
  4. 2:发布者 订阅者 实现类
  5. 3:调用者
  6. 3:基于不同应用场景的不同实现方式
  7. 4:同步阻塞 异步非阻塞 对比
  8. 5:遗留问题

1:观察者模式 入门理解

  1. 观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)。
  2. 在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。
  3. 被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。
  4. 也有以下叫法:
  5. Subject-ObserverPublisher-SubscriberProducer-ConsumerEventEmitter-EventListenerDispatcher-Listener

2:经典代码流程

  1. 1:设置 发布者订阅者 模块方法 Observer Subject
  2. 2:创建 发布者订阅者 实现类 ConcreteSubject ConcreteObserver
  3. 3:订阅者 注册到发布者上 表示监听发布者
  4. 4:给发布者发送消息 然后监听该发布者的所有订阅者 都能收到

1:发布者 与 订阅者 抽象类

  1. public interface Publisher {
  2. //注册订阅者
  3. void registerObserver(Observer observer);
  4. //移除订阅者
  5. void removeObserver(Observer observer);
  6. //给所有发布者发布消息
  7. void notifyObserversSuccess(String message);
  8. //一次性注册所有发布者(可以不用
  9. void registerAllObserver(List<Observer> observers);
  10. }
  1. public interface Observer {
  2. void success(String message);
  3. }

2:发布者 与 订阅者 实现类

  1. public class ConcretePublisher implements Publisher {
  2. private List<Observer> observers = new ArrayList();
  3. @Override
  4. public void registerObserver(Observer observer) {
  5. observers.add(observer);
  6. }
  7. @Override
  8. public void removeObserver(Observer observer) {
  9. observers.remove(observer);
  10. }
  11. // 同步阻塞
  12. @Override
  13. public void notifyObserversSuccess(String message) {
  14. for (Observer observer : observers) {
  15. observer.success(message);
  16. }
  17. }
  18. @Override
  19. public void registerAllObserver(List<Observer> observers) {
  20. this.observers = observers;
  21. }
  22. }
  1. // ConcreteObserverOne ConcreteObserverTwo ConcreteObserverRemove (重复3次 修改类名为以下即可)
  2. public class ConcreteObserver implements Observer{
  3. @Override
  4. public void success(String message) {
  5. //可以根据 发布者的消息 执行自己的逻辑
  6. System.out.println("ConcreteObserver is success." + message);
  7. }
  8. }

3:调用者

  1. //一个个进行订阅者进行新增
  2. private static void testObserverAdd() {
  3. ConcretePublisher subject = new ConcretePublisher();
  4. subject.registerObserver(new ConcreteObserverOne());
  5. subject.registerObserver(new ConcreteObserverTwo());
  6. ConcreteObserverRemove concreteObserverRemove = new ConcreteObserverRemove();
  7. subject.registerObserver(concreteObserverRemove);
  8. subject.removeObserver(concreteObserverRemove);
  9. subject.notifyObserversSuccess("ObserverAdd 成功了");
  10. }
  11. //一次性注册全部订阅者
  12. private static void testObserverAll() {
  13. ConcretePublisher subject = new ConcretePublisher();
  14. List<Observer> observers = new ArrayList<>();
  15. observers.add(new ConcreteObserverOne());
  16. observers.add(new ConcreteObserverTwo());
  17. ConcreteObserverRemove concreteObserverRemove = new ConcreteObserverRemove();
  18. observers.add(concreteObserverRemove);
  19. subject.registerAllObserver(observers);
  20. subject.removeObserver(concreteObserverRemove);
  21. subject.notifyObserversSuccess("ObserverAll 成功了");
  22. }

3:基于不同应用场景的不同实现方式

  1. 观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,
  2. 都有这种模式的影子,比如,邮件订阅、RSS Feeds(站点共享),本质上都是观察者模式。
  3. 应对于普通的请求:
  4. 同步阻塞的实现方式:发布者和订阅者代码在同一个线程内执行,
  5. 发布者一直阻塞,直到所有的订阅者代码都执行完成之后,才执行后续的代码。
  6. 应对于频繁的请求:
  7. 异步非阻塞的实现方式:
  8. 方法1:在订阅者的方法中,使用一个新的线程执行代码
  9. 方法2:基于 EventBus 来实现
  10. 了解:
  11. 如果还需要请求第三方平台(即进程外),那么还可以沿用上面的逻辑,或者用一种更优雅的方式:
  12. 基于消息队列实现(解耦更彻底),但增加了维护成本

4:同步阻塞 和 异步非阻塞 对比

  1. // 同步阻塞
  2. @Override
  3. public void notifyObserversSuccess(String message) {
  4. for (Observer observer : observers) {
  5. observer.success(message);
  6. }
  7. }
  8. // 异步非阻塞 新增线程
  9. @Override
  10. public void notifyObserversSuccess(String message) {
  11. for (Observer observer : observers) {
  12. Thread thread = new Thread(() -> {
  13. observer.success(message);
  14. });
  15. thread.start();
  16. }
  17. }
  18. // 创建线程池
  19. private static ThreadPoolExecutor threadPoolExecutor =
  20. new ThreadPoolExecutor(4, 4, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024));
  21. // 异步非阻塞 新增线程池
  22. @Override
  23. public void notifyObserversSuccess(String message) {
  24. for (Observer observer : observers) {
  25. threadPoolExecutor.execute(() -> observer.success(message));
  26. }
  27. }
  1. 第一种实现方式,频繁地创建和销毁线程比较耗时,并且并发线程数无法控制,创建过多的线程会导致堆栈溢出。
  2. 第二种实现方式,尽管利用了线程池解决了第一种实现方式的问题,但线程池、异步执行逻辑都耦合在了方法里了。

5:遗留问题

  1. 如果我们的需求更加极端一点,需要在同步阻塞和异步非阻塞之间灵活切换,那就要不停地修改 发布者类的代码
  2. 除此之外,如果在项目中,不止一个业务模块需要用到异步非阻塞观察者模式,那这样的代码实现也无法做到复用。
  3. 因此我们引入EventBus 框架

项目连接

  1. 请配合项目代码食用效果更佳:
  2. 项目地址:
  3. https://github.com/hesuijin/hesujin-design-pattern
  4. Git下载地址:
  5. https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.git
  6. demo-study模块 behavior_design_pattern observer