设计模式(一):记一次观察者模式+简单工厂方法模式的实现

一、基本概念

观察者模式(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)具体的业务实现

  1. 退款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失败");
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值