什么是观察者模式
在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知,被依赖对象为被观察者,依赖对象为观察者
在实际开发中有多种叫法::Subject-Observer、Publisher-Subscriber、Producer-Consumer、EventEmitter-EventListener、Dispatcher-Listener
设计模式其实要干的事情就是解耦。创建型模式是将创建和使用代码解耦,结构型模式是将不同功能代码解耦,行为型模式是将不同的行为代码解耦,具体到观察者模式,它是将观察者和被观察者代码解耦。借助设计模式,我们利用更好的代码结构,将一大坨代码拆分成职责更单一的小类,让其满足开闭原则、高内聚松耦合等特性,以此来控制和应对代码的复杂性,提高代码的可扩展性
常见的应用方式
/** * 定义观察者接口 */ public interface Subject { //注册观察者 void registerObserver(Observer observer); //删除观察者 void removeObserver(Observer observer); //发起通知 void notifyObservers(Message message); } /** * 定义被观察者接口 */ public interface Observer { void update(Message message); } public class ConcreteSubject implements Subject { //存储被观察者 private List<Observer> observers = new ArrayList<Observer>(); //注册 @Override public void registerObserver(Observer observer) { observers.add(observer); } //删除 @Override public void removeObserver(Observer observer) { observers.remove(observer); } //发起通知 @Override public void notifyObservers(Message message) { //循环调用被观察者通知方法 for (Observer observer : observers) { observer.update(message); } } } //被观察者实现1 public class ConcreteObserverOne implements Observer { //接收通知 @Override public void update(Message message) { //TODO: 获取消息通知,执行自己的逻辑... System.out.println("ConcreteObserverOne is notified."); } } //被观察者实现 public class ConcreteObserverTwo implements Observer { //接收通知 @Override public void update(Message message) { //TODO: 获取消息通知,执行自己的逻辑... System.out.println("ConcreteObserverTwo is notified."); } } public class Demo { public static void main(String[] args) { //创建一个观察者对象 ConcreteSubject subject = new ConcreteSubject(); //注册被观察者 subject.registerObserver(new ConcreteObserverOne()); subject.registerObserver(new ConcreteObserverTwo()); //发起通知 subject.notifyObservers(new Message()); } }
注册场景应用
需求
假设我们在开发一个 P2P 投资理财系统,用户注册成功之后,我们会给用户发放投资体验金。代码实现大致是下面这个样子的
原始代码
public class UserController { private UserService userService; // 依赖注入 private PromotionService promotionService; // 依赖注入 public Long register(String telephone, String password) { //省略输入参数的校验代码 //注册 long userId = userService.register(telephone, password); //发放体验金 promotionService.issueNewUserExperienceCash(userId); return userId; } }
注册里面做了2个事情一个是注册一个是发放体验金违反单一职责原则,如果不大量更改以下代码可以接收.
如果需要大量更改,比如后续注册成功增加发送短信提醒,我们再register方法增加一行发送短信违反了开闭原则,我们可以通过观察者模式来实现
重构后
//被观察者抽象接口 public interface RegObserver { void handleRegSuccess(long userId); } //发送体验金 public class RegPromotionObserver implements RegObserver { private PromotionService promotionService; // 依赖注入 @Override public void handleRegSuccess(long userId) { promotionService.issueNewUserExperienceCash(userId); } } //发送短信 public class RegNotificationObserver implements RegObserver { private NotificationService notificationService; @Override public void handleRegSuccess(long userId) { notificationService.sendInboxMessage(userId, "Welcome..."); } } public class UserController { private UserService userService; // 依赖注入 //被观察者 private List<RegObserver> regObservers = new ArrayList<>(); // 一次性设置好,之后也不可能动态的修改 public void setRegObservers(List<RegObserver> observers) { regObservers.addAll(observers); } public Long register(String telephone, String password) { //省略输入参数的校验代码 //省略userService.register()异常的try-catch代码 long userId = userService.register(telephone, password); //注册成功 通知被观察者 for (RegObserver observer : regObservers) { observer.handleRegSuccess(userId); } return userId; } }
异步非阻塞模式
可以查看Guava EventBus使用