设计模式-观察者模式

 
摘要:
    观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
 
一、观察者模式定义
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自动添加到主题实现类。

添加使用构造函数:这种方式是观察者主动调用主题实现类添加方法,所以不用订阅方法。这只是两种实现方式。
 
 
四、总结
优点:
察者模式解除主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体
观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。
缺点:
消息的通知默认是顺序执行的,一个观察者的卡顿会影响整体的执行效率,当然在条件允许的情况下可以使用异步解决。
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值