摘要:
观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
一、观察者模式定义
1、定义:
观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主题是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。
2、类图:

角色
(1)、抽象主题AbstractSubject
它把所有观察者对象的引用保存到一个容器里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
(2)、主题实现类:SubjectImpl
将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
(3)、抽象观察者:Observers
为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
(4)、观察者实现类:ObserversImpl1、ObserversImpl2
实现抽象观察者角色所要求的更新接口,接收主题通知,作相应处理。
3、主题-事件-观察者触发关系图:

主题发布事件通知、观察者接收事件并做相应处理……
二、观察者模式使用场景
1、当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象需要被改变。
2、当一个对象必须通知其他对象,而它又不能假定其他对象是谁。换言之,不希望这些对象是紧密耦合的。
3、观察者模式在项目中会有大量使用,比如入库成功要做操作记录日志、发送消息等,如果不需要事务保持一致性则可以使用事件方式,并可以使用异步方式。
4、其实在Tomcat 启动过程大量使用观察者模式……
三、代码实现
使用spring添加观察者(项目代码)
1、事件定义:
public enum BillEventEnum {
/**
* 请款成功事件
*/
SUPPLIER_PAYMENT_SUCCESS,
/**
* 超期费用数据入库
*/
SUPPLIER_EXCEED_RETURN_CHARGE,;
}
2、事件对象(事件参数传递)
@Data
public class BillEvent extends EventObject {
/**
* 事件
*/
private BillEventEnum billEventEnum;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public BillEvent(Object source) {
super(source);
}
public BillEvent(Object source, BillEventEnum billEventEnum) {
super(source);
this.billEventEnum = billEventEnum;
}
}
3、观察者接口
public interface BillListener {
/**
* 事件触发
*
* @param billEvent
*/
void fire(BillEvent billEvent);
/**
* 订阅事件
*
* @return
*/
EnumSet subscribe();
}
4、观察者同步实现抽象类
public abstract class AbstractSyncBillListener implements BillListener {
@Override
public void fire(BillEvent billEvent) {
doHandlerEvent(billEvent);
}
/**
* 具体事件处理
*
* @param billEvent
*/
public abstract void doHandlerEvent(BillEvent billEvent);
}
5、观察者异步实现抽象类
public abstract class AbstractAsyncBillListener implements BillListener {
@Override
public void fire(BillEvent billEvent) {
ThreadUtilsOfCache.getThreadPool().execute(new AsyncHandler(billEvent));
}
/**
* 具体事件处理
*
* @param billEvent
*/
public abstract void doHandlerEvent(BillEvent billEvent);
/**
* 订阅事件
*
* @return
*/
@Override
public abstract EnumSet<BillEventEnum> subscribe();
/***
* 异步处理事件
* @author moke
* @data 520
*/
private class AsyncHandler implements Runnable {
private BillEvent billEvent;
AsyncHandler(BillEvent billEvent) {
this.billEvent = billEvent;
}
@Override
public void run() {
doHandlerEvent(billEvent);
}
}
}
6、观察者同步抽象类实现
@Service
public class SupplierPaymentSuccessEventListener extends AbstractSyncBillListener {
private static final Logger logger = LoggerFactory.getLogger(SupplierPaymentSuccessEventListener.class);
/**
* 入库操作
*/
@Resource
private SupplierRequestForPaymentManagerImpl supplierRequestForPaymentManager;
/**
* 发送mq service
*/
@Resource
McqSendService mcqSendService;
@Resource
private PaymentForSupplierFactory paymentForSupplierFactory;
@Override
public void doHandlerEvent(BillEvent billEvent) {
logger.info("请款入库参数billEvent:{},source:{}", JSON.toJSONString(billEvent.getBillEventEnum()), JSON.toJSONString(billEvent.getSource()));
SupplierPaymentDTO supplierPaymentDTO = (SupplierPaymentDTO) billEvent.getSource();
SupplierOfCusRedDTO supplierOfCusRedDTO = supplierPaymentDTO.getSupplierOfCusRedDTO();
SupplierRequestForPayment supplierRequestForPayment = supplierPaymentDTO.getSupplierRequestForPayment();
Long sourceId = supplierRequestForPayment.getSourceId();
if (null == sourceId) {
supplierRequestForPayment.setSourceId(0L);
}
// 入库前校验重复(这里使用第一篇文章中的策略模式)
PaymentForSupplierStage paymentForSupplierStage = paymentForSupplierFactory.getBySourceType(supplierRequestForPayment.getSourceType());
boolean exists = paymentForSupplierStage.exists(supplierRequestForPayment);
if (exists) {
logger.info("请款重复入库,入库失败");
return;
}
long result = supplierRequestForPaymentManager.insert(supplierRequestForPayment);
logger.info("入库结果result:{},请款金额payAMount:{}", result, payAmount);
}
@Override
public EnumSet<BillEventEnum> subscribe() {
return EnumSet.of(BillEventEnum.SUPPLIER_PAYMENT_SUCCESS);
}
}
7、主题实现类(被观察者)
@Service(value = "billListenerManager")
public class BillListenerManager {
private static final Logger logger = LoggerFactory.getLogger(BillListenerManager.class);
private static EnumMap<BillEventEnum, List<BillListener>> enumMap = new EnumMap<>(BillEventEnum.class);
/**
* 主题发布事件通知
*
* @param billEvent
*/
public void fire(BillEvent billEvent) {
BillEventEnum billEventEnum = billEvent.getBillEventEnum();
List<BillListener> billListenerList = enumMap.get(billEventEnum);
if (CollectionUtils.isEmpty(billListenerList)) {
logger.info("该事件没有相关监听者BillEventEnum:{}", billEventEnum);
return;
}
//通知所有关注该事件的观察者
for (BillListener billListener : billListenerList) {
billListener.fire(billEvent);
}
}
/**
* 添加观察者
*
* @param billEventEnum
* @param billListener
*/
public void addListener(BillEventEnum billEventEnum, BillListener billListener) {
List<BillListener> listeners = enumMap.get(billEventEnum);
if (CollectionUtils.isEmpty(listeners)) {
List<BillListener> list = new ArrayList<>();
list.add(billListener);
enumMap.put(billEventEnum, list);
} else {
listeners.add(billListener);
}
}
}
8、观察者订阅事件
@Component
public class BillListenerFactory implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (null == event) {
return;
}
ApplicationContext applicationContext = event.getApplicationContext();
//父容器、子容器会加载两次
if (null == applicationContext || null != applicationContext.getParent()) {
return;
}
//所有监听类
Map<String, BillListener> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, BillListener.class, false, false);
BillListenerManager billListenerManager = applicationContext.getBean(BillListenerManager.class);
map.forEach((k, listener) -> {
EnumSet<BillEventEnum> enumSet = listener.subscribe();
for (BillEventEnum billEventEnum : enumSet) {
billListenerManager.addListener(billEventEnum, listener);
}
});
}
}
观察者主动添加事件
1、观察者实现类
@Component
public class CacheObjEventListener implements BillListener{
/**
* 添加观察事件
*/
public CacheObjEventListener() {
//订阅多个事件,自己主动订阅,不用定义订阅方法
BillListenerManager.addListener(EventEnum.TRIP_DISPATCHER_SUCCESS_ROB_AFTER_EVENT,this);
BillListenerManager.addListener(EventEnum.MATCHING_SUCCESS_INSTANT_AFTER_EVENT, this);
}
/**
* 事件处理
* @param e
*/
@Override
public void fire(BillEvent e) {
OrderDTO orderDTO = (OrderDTO) e.getSource();
System.out.println("do everything ……");
}
}
2、主题实现类
public class BillListenerManager {
private static EnumMap<BillEventEnum, Vector<BillEventListener>> eventListeners = new EnumMap<>(BillEventEnum.class);
public static void addListener(BillEventEnum event, BillEventListener el) {
Vector<BillEventListener> listeners = eventListeners.get(event);
if (listeners == null) {
listeners = new Vector<>();
eventListeners.put(event, listeners);
} else {
for (BillEventListener l : listeners) {
if (l.getClass() == el.getClass()){
LOG.info("more than one instances added. "+l.getClass());
return;
}
}
}
listeners.addElement(el);
}
/**
* 触发事件
* @param billEvent 事件
* @throws Exception
*/
public static void fire(BillEvent billEvent) {
Vector<BillEventListener> listeners = eventListeners.get(obj.getEvent());
if (listeners == null) {
return;
}
for (BillEventListener listener : listeners) {
listener.fire(billEvent);
}
}
}
添加使用了spring容器:这种在定义的观察者接口时要定义一个方法来获取他要订阅的事件,按照事件spring自动添加到主题实现类。
添加使用构造函数:这种方式是观察者主动调用主题实现类添加方法,所以不用订阅方法。这只是两种实现方式。
四、总结
优点:
观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。
观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。
缺点:
消息的通知默认是顺序执行的,一个观察者的卡顿会影响整体的执行效率,当然在条件允许的情况下可以使用异步解决。
1337

被折叠的 条评论
为什么被折叠?



