一、基本概念
观察者模式(Observer),又叫发布-订阅模式,定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
简单工厂模式(Static Factory Method),定义一个工厂类,他可以根据参数的不同返回不同类的实例
二、观察者模式实现
(1)举例
假设上班时间,有一部分同事上班时间有一部分同事在看股票,一部分同事在看NBA,这时老板回来了,前台通知了部分同事老板回来了,这些同事及时关闭了网页没被发现,而没被通知到的同事被抓了个现行,被老板亲自“通知”关闭网页,UML图如下:
那么我们可以定义为:
观察者Observer
基础的Observer,包含同事姓名等一些基础属性
具体的观察者Observer,就是各个同事
通知者Subject
基础Subject,包含着通知者的一些基础动作,如add()新增观察者,delete()删除通知者,notifyObservers()通知通知者等。
前台作为通知者进行通知动作
public class Client {
2
3 public static void main(String[] args) {
4 //前台为通知者
5 Secretary secretary = new Secretary();
6
7 StockObserver observer = new StockObserver("adam", secretary);
8 NBAObserver observer2 = new NBAObserver("tom", secretary);
9
10 //前台通知
11 secretary.attach(observer);
12 secretary.attach(observer2);
13
14 //adam没被前台通知到,所以被老板抓了个现行
15 secretary.detach(observer);
16
17 //老板回来了
18 secretary.setAction("小心!Boss回来了!");
19 //发通知
20 secretary.notifyObservers();
21 }
22
23 }
三、项目中的实现
1.实现场景
最近开发过程中,接触到了互联网医院-订单模块。
我们知道实际业务中订单有很多类型,如drugOrder药品订单,callOrder电话咨询订单等等。
不同的订单类型,存在着共同的业务动作,比如创建、支付、取消、建立医患关系,发短信通知等等;同时也存在着不同的业务动作,比如药品订单需要扣减库存,而其他类型订单则不需要。
医生取消订单,会同时触发退款,发短信通知,取消医患关系。一种订单类型,也可能会触发多个动作,我们想到了观察者模式
2.代码实现
我们把所有的业务动作,定义为Listener,每种不同的Listener会支持不同订单类型supportType()去使用,执行的具体动作在onChange()中。
在项目初始化时,拿到所有的Listener,构建以OrderTypeEnum为key、List< Listener >为value的Map,listenersMap.在通知方进行通知时,使用工厂模式,根据传入的supportType不同,去构建不同的List< Listener >,进而去调用Listener中的onChange()。
结合概念,supportType视为观察者,listenersMap视为通知者,仔细想想这是不是观察者模式的理念呢?
具体代码:
(1)定义OrderStatusChangeListener接口
public interface OrderStatusChangeListener {
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
/**
* 支持的订单状态(根据订单状态不同,执行不同的动作,具体的check在changeSync中)
* @return
*/
OrderStatusEnum[] supportStatus();
/**
* 越小的数字优先级越高
*
* @return
*/
int order();
void onChange(Orders order, TransactionContext context, OrderEventEnum orderEventEnum);
void changeSync(Orders order, TransactionContext context, OrderEventEnum orderEventEnum);
void changeAsync(Orders order, TransactionContext context, OrderEventEnum orderEventEnum);
/**
* 支持的订单类型
* @return
*/
OrderTypeEnum[] supportType();
}
(2)定义抽象类AbstractStatusChangeListener
public abstract class AbstractStatusChangeListener implements OrderStatusChangeListener {
@Override
public int order() {
return OrderStatusChangeListener.LOWEST_PRECEDENCE;
}
@Override
public void changeSync(Orders order, TransactionContext context, OrderEventEnum orderEventEnum) {
if (ArrayUtils.contains(supportStatus(), order.getOrderStatus())) {
onChange(order, context, orderEventEnum);
}
}
@Override
public void changeAsync(Orders order, TransactionContext context, OrderEventEnum orderEventEnum) {
if (ArrayUtils.contains(supportStatus(), order.getOrderStatus())) {
onChange(order, context, orderEventEnum);
}
}
}
(3)具体的业务实现
- 退款RefundListener
@Service
@Slf4j
@AsyncInterface(isAsyc = false)
public class RefundListener extends AbstractStatusChangeListener {
@Autowired
OrderService orderService;
@Override
public void onChange(Orders order, TransactionContext context, OrderEventEnum orderEventEnum) {
log.info("退_款_广_播_开_始_啦_订_单_Id_{},订_单_状_态_{}", order.getId(), order.getOrderStatus());
switch (order.getOrderType()) {
case packageOrder:
case callOrder:
switch (order.getOrderStatus()) {
case cancel:
refundCancel(order, context);
break;
case cancelByAssistant:
refundCancel(order, context);
break;
case cancelByDoctor:
refundCancel(order, context);
break;
}
case drugOrder:
case askPackageOrder:
case consultationOrder:
default:
switch (order.getOrderStatus()) {
case close:
refundClose(order, context);
break;
}
break;
}
}
@Override
public OrderTypeEnum[] supportType() {
return new OrderTypeEnum[]{
OrderTypeEnum.drugOrder, OrderTypeEnum.callOrder, OrderTypeEnum.packageOrder, OrderTypeEnum.askPackageOrder, OrderTypeEnum.consultationOrder
};
}
/**
* 关闭退款
*/
private void refundClose(Orders order, TransactionContext context) {
String closeReason = (String) context.getData(BaseStateMachineKey.CLOSE_REASON);
orderService.refund(
order.getId(), order.getUserId(), closeReason == null ? "订单关闭" : closeReason, null);
}
/**
* 取消退款
*
* @param order
* @param context
*/
private void refundCancel(Orders order, TransactionContext context) {
String cancelReason = (String) context.getData(BaseStateMachineKey.CANCEL_REASON);
orderService.refund(
order.getId(), order.getUserId(), cancelReason == null ? "用户取消退款" : cancelReason, null);
}
@Override
public OrderStatusEnum[] supportStatus() {
return new OrderStatusEnum[]{
OrderStatusEnum.close,
OrderStatusEnum.cancel,
OrderStatusEnum.cancelByAssistant,
OrderStatusEnum.cancelByDoctor
};
}
}
(4)使用ApplicationListener初始化OrderStatusChangeListener工厂数据
4.1 ApplicationListener简单介绍:
ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。
如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发。这种事件机制都必须需要程序显示的触发
其中spring有一些内置的事件,当完成某种操作时会发出某些事件动作。比如监听ContextRefreshedEvent事件,当所有的bean都初始化完成并被成功装载后会触发该事件,实现ApplicationListener接口可以收到监听动作
4.2构建OrderStatusChangeListener工厂数据,等同于根据创建通知者Subject
@Service
@Slf4j
public class SimpleOrderStatusChangeMulticaster implements OrderStatusChangeMulticaster,
ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
/**
* 业务线,状态改变时监听器
*/
private Map<OrderTypeEnum, List<OrderStatusChangeListener>> listenersMap = new HashMap<>();
/**
* 初始化所有监听器
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Map<String, OrderStatusChangeListener> listenerBeans = appContext.getBeansOfType(OrderStatusChangeListener.class);
if (null == listenerBeans || listenerBeans.size() == 0) {
return;
}
//便利所有的实现类
listenerBeans.values().stream().sorted((l1, l2) -> l1.order() - l2.order()).forEach(l ->
Arrays.stream(l.supportType()).forEach(b -> {
List<OrderStatusChangeListener> listeners = listenersMap.get(b);
if (null == listeners) {
listeners = new LinkedList<>();
}
listeners.add(l);
listenersMap.put(b, listeners);
}));
}
}
(5)执行通知multicast(),支持同步和异步方式,异步方式需要使用CountDownLatch来判断异步任务的执行状态
@Override
public void multicast(Orders order, TransactionContext context, OrderEventEnum orderEventEnum) {
log.info("____订单进入广播");
Boolean isSync = (Boolean) context.getData(BaseStateMachineKey.IS_SYNC);
/** 如果没有顺序要求,默认异步执行*/
if (isSync == null) {
isSync = false;
}
// if (order.getOrderType().equals(OrderTypeEnum.askPackageOrder)
// || order.getOrderType().equals(OrderTypeEnum.consultationOrder)) {
// isSync = true;//问诊订单和追问包设置为同步
// }
//根据订单类型不同,拿到所有的业务方法Listener
List<OrderStatusChangeListener> listeners = listenersMap.get(order.getOrderType());
if (null == listeners || listeners.size() == 0) {
return;
}
/**
* handler可以配置整个流程的同步异步
* listener可以配置单独异步,优先级高于handler
*/
List<OrderStatusChangeListener> listenersSyncAnno = new LinkedList<>();
List<OrderStatusChangeListener> listenersAsncAnno = new LinkedList<>();
for (OrderStatusChangeListener l : listeners) {
AsyncInterface annotationsByType = l.getClass().getAnnotation(AsyncInterface.class);
if (annotationsByType != null) {
boolean isAsycListener = annotationsByType.isAsyc();
if (isAsycListener) {
listenersAsncAnno.add(l);
} else {
listenersSyncAnno.add(l);
}
} else {
if (isSync) {
listenersSyncAnno.add(l);
} else {
listenersAsncAnno.add(l);
}
}
}
//同步执行
listenersSyncAnno.stream().forEach(l -> l.changeSync(order, context, orderEventEnum));
//异步执行
asncCall(listenersAsncAnno, order, context, orderEventEnum);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.appContext = applicationContext;
}
private void asncCall(List<OrderStatusChangeListener> listeners, Orders order, TransactionContext context, OrderEventEnum orderEventEnum) {
init();
CountDownLatch latch = new CountDownLatch(listeners.size());
listeners.stream().forEach(l -> {
AsncMulticasterJob job = new AsncMulticasterJob();
job.init(l, latch, order, context, orderEventEnum);
executorService.submit(job);
});
/** 需要保持一个事物的设置为同步并且在广播里面做异常抛出*/
try {
boolean await = latch.await(5000, TimeUnit.MILLISECONDS);
if (!await) {
log.error("计数器超时,订单id为:{},状态为:{}", order.getId(), order.getOrderStatus());
}
} catch (InterruptedException e) {
log.error("countdownLatch失败");
}
}