观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
观察者模式包含以下几个核心角色:
观察者模式通过将主题和观察者解耦,实现了对象之间的松耦合。当主题的状态发生改变时,所有依赖于它的观察者都会收到通知并进行相应的更新。
1.事件监听接口
public interface EventListener {
void doEvent(LotteryResult result);
}
2.实现监听接口(短信、MQ)
public class MessageEventListener implements EventListener {
private Logger logger = LoggerFactory.getLogger(MessageEventListener.class);
@Override
public void doEvent(LotteryResult result) {
logger.info("给⽤户 {} 发送短信通知(短信):{}", result.getuId(),
result.getMsg());
}
}
public class MQEventListener implements EventListener {
private Logger logger = LoggerFactory.getLogger(MQEventListener.class);
@Override
public void doEvent(LotteryResult result) {
logger.info("记录⽤户 {} 摇号结果(MQ):{}", result.getuId(),
result.getMsg());
}
}
3.事件管理类
public class EventManager {
Map, List> listeners = new HashMap<>();
public EventManager(Enum... operations) {
for (Enum operation : operations) {
this.listeners.put(operation, new ArrayList<>());
}
}
public enum EventType {
MQ, Message
}
/**
* 订阅
*
* @param eventType 事件类型
* @param listener 监听
*/
public void subscribe(Enum eventType, EventListener
listener) {
List users = listeners.get(eventType);
users.add(listener);
}
/**
* 取消订阅
*
* @param eventType 事件类型
* @param listener 监听
*/
public void unsubscribe(Enum eventType, EventListener
listener) {
List users = listeners.get(eventType);
users.remove(listener);
}
/**
* 通知
*
* @param eventType 事件类型
* @param result 结果
*/
public void notify(Enum eventType, LotteryResult result) {
List users = listeners.get(eventType);
for (EventListener listener : users) {
listener.doEvent(result);
}
}
}
整个处理的实现上提供了三个主要⽅法;订阅( subscribe )、取消订阅( unsubscribe )、通知 ( notify )。这三个⽅法分别⽤于对监听时间的添加和使⽤。
另外因为事件有不同的类型,这⾥使⽤了枚举的⽅式进⾏处理,也⽅便让外部在规定下使⽤事件, ⽽不⾄于乱传信息( EventType.MQ 、 EventType.Message )。
4.业务抽象类接⼝
public abstract class LotteryService {
private EventManager eventManager;
public LotteryService() {
eventManager = new EventManager(EventManager.EventType.MQ,
EventManager.EventType.Message);
eventManager.subscribe(EventManager.EventType.MQ, new
MQEventListener());
eventManager.subscribe(EventManager.EventType.Message, new
MessageEventListener());
}
public LotteryResult draw(String uId) {
LotteryResult lotteryResult = doDraw(uId);
// 需要什么通知就给调⽤什么⽅法
eventManager.notify(EventManager.EventType.MQ, lotteryResult);
eventManager.notify(EventManager.EventType.Message,
lotteryResult);
return lotteryResult;
}
protected abstract LotteryResult doDraw(String uId);
}
5.业务接⼝实现类
public class LotteryServiceImpl extends LotteryService {
private MinibusTargetService minibusTargetService = new
MinibusTargetService();
@Override
protected LotteryResult doDraw(String uId) {
// 摇号
String lottery = minibusTargetService.lottery(uId);
// 结果
return new LotteryResult(uId, lottery, new Date());
}
}
6.测试
@Test
public void test(){
LotteryService lotteryService=new LotteryServiceImpl();
LotteryResult result=lotteryService.draw("2765789109876");
logger.info("测试结果:{}",JSON.toJSONString(result));
}
参考:观察者模式 | 菜鸟教程
《重学Java设计模式》