設計模式——觀察者模式

《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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章