观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监察一个主题对象,主题对象在状态上的变化能够通知所有的依赖于此对象的那些观察者对象。
引子:一个网上商场,有一个商品的名字写错了,需要修改。那么修改一个商品的名字,需要通知到搜索引擎服务,也需要通知仓库修改商品名。(当然应该用消息队列,这里用于演示)
第一版实现:
通知搜索服务
//修改商品名称对应的搜索
public class NotifySearch {
public void amendProductName(Product product){
System.out.println("search new name "+product.getName());
}
}
通知仓库
public class NotifyWarehouse {
public void amendProductName(Product product){
System.out.println("ware house amend product name " + product.getName());
}
}
商品类:
public class Product {
private String name;
NotifySearch search = new NotifySearch();
NotifyWarehouse warehouse = new NotifyWarehouse();
//修改名称
public void amendName(String name) {
this.name = name;
search.amendProductName(this);
warehouse.amendProductName(this);
}
//getter setter 构造方法
}
在商品类中,有NotifySearch NotifyWarehouse 引用,修改商品名称的时候,调用一下。
问题:
1、如果商品名称修改时,又要通知进货渠道修改名字,就要修改Product 类
2、当商品的价格修改时,也需要通知搜索服务和仓库,那就还需要增加两个类Notify 类,并且要修改Product 类
这个时候就需要,把给商品注册一监观察者(或者叫监听器),当商品发生变化的时候这些观察者就会做出响应。
第二版实现:
商品名称观察者
//商品名称观察者
public interface ProductNameObserver {
void amendProductName(Product product);
}
修改NotifySearch NotifyWarehouse 实现此观察者
//修改商品名称对应的搜索
public class NotifySearch implements ProductNameObserver{
public void amendProductName(Product product){
System.out.println("search new name "+product.getName());
}
}
public class NotifyWarehouse implements ProductNameObserver{
public void amendProductName(Product product){
System.out.println("ware house amend product name " + product.getName());
}
}
商品类是需要添加一个观察者的List,修改如下:
public class Product {
private String name;
List<ProductNameObserver> nameObserverList = new ArrayList<>();
//修改名称
public void amendName(String name) {
this.name = name;
//观察者
for (ProductNameObserver productNameObserver : nameObserverList) {
productNameObserver.amendProductName(this);
}
}
public void addNameObserver(ProductNameObserver nameObserver) {
nameObserverList.add(nameObserver);
}
//getter setter 构造方法
}
测试:
public class Test {
public static void main(String[] args) {
Product product = new Product("001 号商品");
product.addNameObserver(new NotifySearch());
product.addNameObserver(new NotifyWarehouse());
product.amendName("000 号商品");
}
}
商品类添加观察者的代码和商品没有关系的,所以可以抽象出来:
第三版实现:
在第二版的基础上添加可观察者类
//可观察的
public class TObservable {
private List<ProductNameObserver> nameObserverList = new ArrayList<>();
public void addNameObserver(ProductNameObserver nameObserver) {
nameObserverList.add(nameObserver);
}
public List<ProductNameObserver> getNameObserverList() {
return nameObserverList;
}
}
商品类修改:
继承 TObservable 类,说明这个类是可观察的
调用观察者的地方修改一下,其它的不用修改
public class Product extends TObservable {
private String name;
//修改名称
public void amendName(String name) {
this.name = name;
//观察者
for (ProductNameObserver productNameObserver : getNameObserverList()) {
productNameObserver.amendProductName(this);
}
}
//getter setter 构造方法
}
UML 图
ConcreteObservable 实现 Observable , Observable 定义了添加Observer 的相关操作
ObserverA 观察者是实现了 Observer , 是具体的观察者
如果观察者模式其中一个结点处理完就中断的话,就是像责任链模式。
JDK 中实现对应的类:
Observable Observer
优点:
观察者和被观察者都容易扩展
缺点:
一个被观察者,多个观察者,开发和调度比较复杂。
java 中调用是顺序调用的,如果有一个执行不了,会影响整个的执行效率。
使用场景:
关联行为场景,这个行为是可以拆分的
事件多触发场景
跨系统的消息交换,如消息队列机制