标签 : Java与设计模式
观察者模式: 又称‘发布-订阅’模式, 定义一种对象间的一对多依赖关系(多个观察者Observer监听某一主题Subject). 当主题状态发生改变时,所有依赖它的对象都得到通知并被自动更新.
核心: 触发联动(图片来源: 设计模式: 可复用面向对象软件的基础)
以电商系统下单:
用户购买某件商品下一个订单, 需要: 通知库存系统减少库存、通知商家系统发货、通知支付系统收钱、甚至还会通知关系中心使当前用户关注该商家.
目标/主题/抽象通知者:
/**
* @author jifang
* @since 16/8/30 上午9:49.
*/
public abstract class Subject {
protected List observers = new LinkedList<>();
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
protected abstract void notifyObservers();
public abstract String getState();
public abstract void setState(String state);
}
具体目标/具体主题:
class OrderSubject extends Subject {
private String state;
/**
* 采用拉模型, 将Subject自身发送给Observer
*/
@Override
protected void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}
@Override
public String getState() {
return state;
}
@Override
public void setState(String state) {
this.state = state;
this.notifyObservers();
}
}
抽象观察者: 为那些在目标状态发生改变时需获得通知的对象定义一个更新接口.
public interface Observer {
void update(Subject subject);
}
具体观察者:
class WareHouseObserver implements Observer {
private String orderState;
@Override
public void update(Subject subject) {
orderState = subject.getState();
System.out.println("库存系统接收到消息 [" + orderState + "], 减少库存");
}
}
class PayObserver implements Observer {
private String orderState;
@Override
public void update(Subject subject) {
orderState = subject.getState();
System.out.println("支付系统接收到消息 [" + orderState + "], 正在收钱");
}
}
class RelationObserver implements Observer {
private String orderState;
@Override
public void update(Subject subject) {
orderState = subject.getState();
if (orderState.equals("已付款")) {
System.out.println("关系系统接收到消息 [" + orderState + "], 当前用户已关注该店铺");
} else if (orderState.equals("取消订单")) {
System.out.println("关系系统接收到消息 [" + orderState + "], 当前用户取消关注该店铺");
}
}
}
public class Client {
@Test
public void client() {
Subject subject = new OrderSubject();
Observer payObserver = new PayObserver();
Observer relationObserver = new RelationObserver();
Observer wareHouseObserver = new WareHouseObserver();
// 注册到Subject
subject.attach(payObserver);
subject.attach(relationObserver);
subject.attach(wareHouseObserver);
subject.setState("已付款");
System.out.println("-------------");
// 付钱、发货完成
subject.detach(payObserver);
subject.detach(wareHouseObserver);
subject.setState("取消订单");
}
}
以上我们采用的是拉模型实现Subject对Observer通知(传递Subject自身), 在观察者模式中还有一种推模型实现:
this
)通过update()
方法传递给观察者, 观察者只要知道有通知到来即可, 至于什么时候获取什么内容都可自主决定.对比
推模型中假定Subject知道观察者需要数据的详细信息, 而拉模型中Subject不需要知道观察者具体需要什么数据(因此把自身传过去, 由观察者取值).因此:
update()
方法参数是Subject本身, 基本上可以适应各种情况的需要.Java语言自身提供了对观察者模式的支持: java.util
包下提供了Observable
类与Observer
接口. 下面我们就用Java的支持实现观察者模式的推模型:
public class OrderSubject extends Observable {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
this.setChanged();
this.notifyObservers(state);
}
@Override
public String toString() {
String result = "OrderSubject{";
try {
Field obs = Observable.class.getDeclaredField("obs");
obs.setAccessible(true);
Vector vector = (Vector) obs.get(this);
result += vector;
} catch (NoSuchFieldException | IllegalAccessException ignored) {
}
return result +
"state='" + state + '\'' +
'}';
}
}
public class WareHouseObserver implements Observer {
private String orderState;
@Override
public void update(Observable o, Object arg) {
System.out.println("拉模式: " + o);
orderState = (String) arg;
System.out.println("推模式: 库存系统接收到消息 [" + orderState + "], 减少库存");
}
}
Guava提供
EventBus
以取代发布者和订阅者之间的显式注册, 取而代之的是使用注解@Subscribe
, 使组件间有更好的解耦.
封装消息类
EventBus的Event
继承: EventBus自动把事件分发给事件超类的监听者/观察者,并允许监听者声明监听接口类型和泛型的通配符类型(如 ? super Xxx
).
interface Event {
String getState();
}
- 注:
- DeadEvent : 如果EventBus发送的消息都不是订阅者关心的称之为DeadEvent.
- 每个用
@Subscribe
注解标注的方法只能有一个参数.
使用Guava之后, 如果要订阅消息, 就不用再实现指定的接口, 只需在指定的方法上加上@Subscribe
注解即可, 但为了代码的易读性, 我们还是推荐保留公共的接口:
public interface Observer {
void update(Event event);
}
EventBus.post(Object)
方法即可, 异步分发可以直接用EventBus
子类AsyncEventBus
.public class Producer {
private static final Logger LOGGER = Logger.getLogger(Client.class.getName());
@Test
public void client() {
EventBus bus = new EventBus("observer-pattern");
bus.register(new Observer() {
@Subscribe
@Override
public void update(Event event) {
System.out.println("库存系统接收到消息 [" + event.getState() + "], 减少库存");
}
});
bus.register(new Observer() {
@Subscribe
@Override
public void update(Event event) {
System.out.println("支付系统接收到消息 [" + event.getState() + "], 正在收钱");
}
});
// 不用实现接口, 直接给出一个Object对象也可
bus.register(new Object() {
@Subscribe
public void onEvent(Event event) {
System.out.println("关系系统接收到消息 [" + event.getState() + "], 当前用户关注店铺");
}
@Subscribe
public void onEventFun(Event event) {
System.out.println("我就是来打酱油的o(╯□╰)o");
}
});
// 注册DeadEvent
bus.register(new Object() {
@Subscribe
public void onDead(DeadEvent dead) {
LOGGER.log(Level.WARNING, "没有消费者接收" + dead);
}
});
// 发布消息
bus.post(new Event() {
@Override
public String getState() {
return "付钱成功";
}
});
bus.post("dead event O(∩_∩)O~");
}
}
注: 线程间通信框架Disruptor也是观察者模式的一种具体实现, 详细可参考博客: Java并发基础:Disruptor小结、并发框架Disruptor译文.
将系统分割成一系列相互协作的类有一定的副作用: 需要维护相关对象间的一致性, 我们不希望为了一致性而将各类紧密耦合, 这样会给维护、扩展和重用都带来不便.
而观察者模式允许独立的改变目标和观察者. 你可以单独复用Subject而不用管Observer 反之亦然. 它也使你可以在不改动Subject和其他Observer的前提下增加观察者.
Listener
;