设计模式——观察者模式

《Head First 设计模式》 学习笔记,码云同步更新中

如有错误或不足之处,请一定指出,谢谢~

往期回顾

  1. 设计模式——策略模式
  2. 设计模式——装饰者模式
  3. 设计模式——观察者模式

观察者模式

  • 定义:
    • 定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新
  • 结构:
    • Subject:主题接口
    • ConcreteSubject:具体主题
    • Observer:观察者接口
    • ConcreteObserver:具体观察者
  • 优点:
    • 具体主题和具体观察者之间为松耦合关系
    • 符合“开闭原则”
  • 缺点:
    • 没有相应的机制使观察者知道主题是如何发生变化的
    • 如果观察者之间存在循环调用,会导致系统崩溃,需要特别注意
  • 适用范围:
    • 一个对象的改变需要触发其他多个对象的改变,但不知道具体有多少需要改变的对象,降低对象间的耦合
    • 一个对象需要通知很多其他对象,但不需要知道他们是谁
  • 与发布-订阅模式的区别
    • 发布-订阅模式中,发布者不直接和订阅者通信,他们甚至不知道对方的存在。他们通过第三方信息中介进行通信
    • 观察者模式大多是同步的,而发布-订阅模式大多是异步的(消息队列)
  • 其他:
    • 在另一个例子中:气象台(主题)发布实时数据(间隔很短,假设1秒一次),
      布告板(观察者)显示气温(有可能实时,也有可能每天一次,或者统计月平均数据)。
      这种情况下观察者需要将接收到的数据缓存下来,然后在各自设定的时间对外展示。
    • 在JDK的java.util包中,提供了Observable类以及Observer接口,它们构成了Java语言对观察者模式的支持。
  • 案例:
    • 线上商城客户下单付款后,需要执行一系列业务逻辑,随时有可能增加或删去
      1. 发送短信
      2. 累计积分
      3. 购物节活动赠送小礼品
    • 原有实现:在付款成功后的方法中增加相关代码,等到不需要时再删除代码。
    • 问题:反复增删改付款核心逻辑,易出错,难维护。
    • 观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新
      • 创建观察者(通知)接口、被观察者(主题)接口
      • 分别实现这两个接口——订单付款主题,返现观察者,发送信息观察者等等…
      • 创建订单付款后处理器,可交由Spring管理,通过配置文件配置观察者向主题进行注册
      • 在原订单付款后方法中,掉用处理器方法,让主题发出通知,各观察者接收通知执行各自逻辑
  • 代码:
/**
 * 主题
 */
public interface Subject<T> {
    /**
     * 观察者注册
     * @param o
     */
    void registerObserver(Observer o);

    /**
     * 观察者退出
     * @param o
     */
    void removeObserver(Observer o);

    /**
     * 发出通知
     * @param t
     */
    void notifyObservers(T t);
}

/**
 * 订单支付 主题
 **/
public class OrderPaidSubject implements Subject<Order> {
    /**
     * 观察者名单
     */
    private ArrayList<Observer> observerList;

    public OrderPaidSubject() {
        observerList = new ArrayList<>();
    }

    @Override
    public void registerObserver(Observer o) {
        observerList.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        observerList.remove(o);
    }

    /**
     * 遍历观察者名单发送通知
     * @param order
     */
    @Override
    public void notifyObservers(Order order) {
        observerList.forEach(observer -> observer.update(order));
    }
}

/**
 * 观察者
 */
public interface Observer<T> {
    /**
     * 接收通知
     */
    void update(T t);
}

/**
 * 返现观察者
 **/
public class CashBackOB implements Observer<Order> {
    @Override
    public void update(Order order) {
        System.out.println("返现5元,订单编号:" + order.getOrderNo());
    }
}

/**
 * 发送短信观察者
 **/
public class SendMsgOB implements Observer<Order> {
    @Override
    public void update(Order order) {
        System.out.println("发送短信,订单编号:" + order.getOrderNo());
    }
}

/**
 * 订单
 **/
@Data
public class Order {
    private Integer orderId;
    private String orderNo;
}

/**
 * 订单支付后处理
 **/
public class OrderPaidHandler {

    private OrderPaidSubject orderPaidSubject;

    public OrderPaidHandler() {
        orderPaidSubject = new OrderPaidSubject();
        // 可改造为 通过配置文件 注册
        orderPaidSubject.registerObserver(new CashBackOB());
        orderPaidSubject.registerObserver(new SendMsgOB());
    }

    public void orderIsPaid(Order order) {
        orderPaidSubject.notifyObservers(order);
    }
}

/**
 * 测试类
 **/
public class Test {
    public static void main(String[] args) {
        OrderPaidHandler orderPaidHandler = new OrderPaidHandler();
        Order order = new Order();
        order.setOrderId(1);
        order.setOrderNo("01012254");
        orderPaidHandler.orderIsPaid(order);
    }
}

结果:
    返现5元,订单编号:01012254
    发送短信,订单编号:01012254
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章