观察者设计模式也被称为监听者模式或发布订阅模式。在 GoF 的《设计模式》一书中,它的定义是这样的:“在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知”。
观察者需要注册到被观察者中,被观察者状态更新需要调用观察者update() 方法。但是,在跨进程的实现方式中,我们可以利用消息队列实现彻底解耦,观察者和被观察者都只需要跟消息队列交互,观察者完全不知道被观者的存在,被观察者也完全不知道观察者的存在。
可以看出,这样的代码非常的优雅,主逻辑(商品类主题变价)和其他逻辑(比如价格观察者的逻辑处理、产品id处理等等)进行了解耦,实现一对多,只要变价,就会广播消息,观察者就可以执行自己的逻辑。
具体代码实现如下:
/**
* 商品
*/
@Data
public class Goods {
/**
* 商品id
*/
private long id;
/**
* 价格
*/
private String price;
}
定义和实现主题类
/**
* 订阅的主题
*/
public interface Subject<T> {
/**
* 注册观察者
*/
void registerObserver(Observer observer);
/**
* 通知观察者
*/
void notifyObserver(Goods goods);
}
/**
* 商品主题
*/
@Slf4j
public class GoodsSubject implements Subject<Goods>{
/**
* 观察者list
*/
private List<Observer> list = Lists.newArrayList();
@Override
public void registerObserver(Observer observer) {
list.add(observer);
}
@Override
public void notifyObserver(Goods goods) {
list.forEach(e -> e.executeMethod(goods));
}
/**
* 价格变动
*/
public void changePrice(Goods goods) {
log.info("商品价格变动");
this.notifyObserver(goods); //通知到观察者
}
}
然后定义和实现观察者
/**
* 观察者接口
*/
public interface Observer {
/**
* 执行方法
*/
void executeMethod(Goods goods);
}
/**
* 价格观察者
*/
@Slf4j
public class PriceObserver implements Observer{
@Override
public void executeMethod(Goods goods) {
log.info("对商品价格进行处理…… price = " + goods.getPrice());
}
}
进行测试
@Test
public void testObserver() {
//变动的商品
Goods goods = new Goods();
goods.setId(10L);
goods.setPrice("200");
//商品主题
GoodsSubject goodsSubject = new GoodsSubject();
//注册观察者
PriceObserver priceObserver = new PriceObserver();
goodsSubject.registerObserver(priceObserver);
//执行变价
goodsSubject.changePrice(goods);
}
打印结果:
[main] INFO c.j.o.GoodsSubject - 商品价格变动
[main] INFO c.j.o.PriceObserver - 对商品价格进行处理…… price = 200
这个案例提供的是同步阻塞式
观察者模式,也可以单独开线程处理观察者逻辑,即异步非阻塞
。
在SpringBoot中,可以使用ApplicationContext
实现事件的发布,使用@EventListener
实现事件监听,可以额外使用@Async("threadPoolUtil")
实现异步监听。参考《SpringBoot应用——项目搭建指北》的Spring的异步监听机制
章节。
Spring的监听,万变不离其宗,不过采用的形式不一样,Spring是通过消息发送方法参数类型和消息监听方法参数类型进行反射匹配,组合到一起的。容器启动的时候会将带有@EventListener
注解的类注册到观察者列表中,当使用ApplicationContext
发送消息的时候,会根据消息的类型Object
反射判断,应该由哪个观察者进行处理。