目录与学习目标
1:观察者模式 入门理解2:经典代码流程 1:发布者 与 订阅者 抽象类 2:发布者 与 订阅者 实现类 3:调用者3:基于不同应用场景的不同实现方式4:同步阻塞 和 异步非阻塞 对比5:遗留问题
1:观察者模式 入门理解
观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)。在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。也有以下叫法:Subject-Observer、Publisher-Subscriber、Producer-Consumer、EventEmitter-EventListener、Dispatcher-Listener
2:经典代码流程
1:设置 发布者订阅者 模块方法 Observer Subject 2:创建 发布者订阅者 实现类 ConcreteSubject ConcreteObserver 3:订阅者 注册到发布者上 表示监听发布者 4:给发布者发送消息 然后监听该发布者的所有订阅者 都能收到
1:发布者 与 订阅者 抽象类
public interface Publisher { //注册订阅者 void registerObserver(Observer observer); //移除订阅者 void removeObserver(Observer observer); //给所有发布者发布消息 void notifyObserversSuccess(String message); //一次性注册所有发布者(可以不用 void registerAllObserver(List<Observer> observers);}
public interface Observer { void success(String message);}
2:发布者 与 订阅者 实现类
public class ConcretePublisher implements Publisher { private List<Observer> observers = new ArrayList(); @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { observers.remove(observer); } // 同步阻塞 @Override public void notifyObserversSuccess(String message) { for (Observer observer : observers) { observer.success(message); } } @Override public void registerAllObserver(List<Observer> observers) { this.observers = observers; }}
// ConcreteObserverOne ConcreteObserverTwo ConcreteObserverRemove (重复3次 修改类名为以下即可)public class ConcreteObserver implements Observer{ @Override public void success(String message) { //可以根据 发布者的消息 执行自己的逻辑 System.out.println("ConcreteObserver is success." + message); }}
3:调用者
//一个个进行订阅者进行新增 private static void testObserverAdd() { ConcretePublisher subject = new ConcretePublisher(); subject.registerObserver(new ConcreteObserverOne()); subject.registerObserver(new ConcreteObserverTwo()); ConcreteObserverRemove concreteObserverRemove = new ConcreteObserverRemove(); subject.registerObserver(concreteObserverRemove); subject.removeObserver(concreteObserverRemove); subject.notifyObserversSuccess("ObserverAdd 成功了"); } //一次性注册全部订阅者 private static void testObserverAll() { ConcretePublisher subject = new ConcretePublisher(); List<Observer> observers = new ArrayList<>(); observers.add(new ConcreteObserverOne()); observers.add(new ConcreteObserverTwo()); ConcreteObserverRemove concreteObserverRemove = new ConcreteObserverRemove(); observers.add(concreteObserverRemove); subject.registerAllObserver(observers); subject.removeObserver(concreteObserverRemove); subject.notifyObserversSuccess("ObserverAll 成功了"); }
3:基于不同应用场景的不同实现方式
观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,都有这种模式的影子,比如,邮件订阅、RSS Feeds(站点共享),本质上都是观察者模式。应对于普通的请求:同步阻塞的实现方式:发布者和订阅者代码在同一个线程内执行,发布者一直阻塞,直到所有的订阅者代码都执行完成之后,才执行后续的代码。应对于频繁的请求:异步非阻塞的实现方式: 方法1:在订阅者的方法中,使用一个新的线程执行代码 方法2:基于 EventBus 来实现了解:如果还需要请求第三方平台(即进程外),那么还可以沿用上面的逻辑,或者用一种更优雅的方式:基于消息队列实现(解耦更彻底),但增加了维护成本
4:同步阻塞 和 异步非阻塞 对比
// 同步阻塞 @Override public void notifyObserversSuccess(String message) { for (Observer observer : observers) { observer.success(message); } } // 异步非阻塞 新增线程 @Override public void notifyObserversSuccess(String message) { for (Observer observer : observers) { Thread thread = new Thread(() -> { observer.success(message); }); thread.start(); } } // 创建线程池 private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 4, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024)); // 异步非阻塞 新增线程池 @Override public void notifyObserversSuccess(String message) { for (Observer observer : observers) { threadPoolExecutor.execute(() -> observer.success(message)); } }
第一种实现方式,频繁地创建和销毁线程比较耗时,并且并发线程数无法控制,创建过多的线程会导致堆栈溢出。第二种实现方式,尽管利用了线程池解决了第一种实现方式的问题,但线程池、异步执行逻辑都耦合在了方法里了。
5:遗留问题
如果我们的需求更加极端一点,需要在同步阻塞和异步非阻塞之间灵活切换,那就要不停地修改 发布者类的代码除此之外,如果在项目中,不止一个业务模块需要用到异步非阻塞观察者模式,那这样的代码实现也无法做到复用。因此我们引入EventBus 框架
项目连接
请配合项目代码食用效果更佳:项目地址:https://github.com/hesuijin/hesujin-design-patternGit下载地址:https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.gitdemo-study模块 下 behavior_design_pattern observer包