首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 觀察者模式實戰:以微信公眾號推送為例

     編輯:Java 相關知識

前言:生活中的觀察者模式

還記得警匪片中,強盜團夥如何配合作案嗎?幾個人在室內行竊,總有一兩個人在門口「放風」。一旦有風吹草動,放風者會立即通知裡面的夥伴緊急撤退。值得注意的是,放風者不一定認識裡面的每一個夥伴,新來的小弟可能也不認識放風者,但這不影響他們之間的溝通,因為他們之間有預先約定好的暗號。這正是觀察者模式在現實生活中的生動寫照。

什麼是觀察者模式?

觀察者模式又稱發布-訂閱模式。GoF 給出的定義是:定義對象之間的一種一對多依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都會得到通知並自動更新。

在深入之前,先回顧一個面向對象設計的核心原則——單一職責原則。系統中的每個對象應專注於領域中的單一職責,理想情況下一個對象只做一件事。這帶來了復用性、可維護性,也是重構的良好基礎。幾乎所有設計模式都基於此原則。觀察者模式的起源常被認為在 GUI 與業務數據的分離上,但其應用遠不止於此。

場景模擬:微信公眾號推送

以當下火熱的微信服務號為背景:

  • 服務號 = 主題
  • 用戶 = 觀察者
  • 功能:用戶訂閱服務號,有新消息即推送;取消訂閱則不再接收;只要服務號存在,就會持續有訂閱者。

類圖結構

核心角色包括:Subject(主題接口)、Observer(觀察者接口)、ConcreteSubject(具體主題)、ConcreteObserver(具體觀察者)。

代碼實現(自定義版)

1. 定義接口

package com.zhy.pattern.observer;

/** 主題接口,所有主題必須實現 */
public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

package com.zhy.pattern.observer;

/** 觀察者接口 */
public interface Observer {
    void update(String msg);
}

2. 具體主題:3D 彩票服務號

package com.zhy.pattern.observer;
import java.util.ArrayList;
import java.util.List;

public class ObjectFor3D implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String msg; // 3D 號碼

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        int index = observers.indexOf(observer);
        if (index >= 0) observers.remove(index);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(msg);
        }
    }

    /** 主題更新消息 */
    public void setMsg(String msg) {
        this.msg = msg;
        notifyObservers();
    }
}

3. 具體觀察者:兩位用戶

package com.zhy.pattern.observer;

public class Observer1 implements Observer {
    private Subject subject;
    public Observer1(Subject subject) {
        this.subject = subject;
        subject.registerObserver(this);
    }
    @Override
    public void update(String msg) {
        System.out.println("observer1 獲得 3D 號碼 -->" + msg + ", 我要記下來。");
    }
}

package com.zhy.pattern.observer;

public class Observer2 implements Observer {
    private Subject subject;
    public Observer2(Subject subject) {
        this.subject = subject;
        subject.registerObserver(this);
    }
    @Override
    public void update(String msg) {
        System.out.println("observer2 獲得 3D 號碼 -->" + msg + "我要告訴室友們。");
    }
}

4. 測試類

package com.zhy.pattern.observer.test;
import com.zhy.pattern.observer.*;

public class Test {
    public static void main(String[] args) {
        ObjectFor3D subjectFor3d = new ObjectFor3D();
        Observer observer1 = new Observer1(subjectFor3d);
        Observer observer2 = new Observer2(subjectFor3d);

        subjectFor3d.setMsg("20140420的3D號碼是:127");
        subjectFor3d.setMsg("20140421的3D號碼是:333");
    }
}

5. 輸出結果

observer1 獲得 3D 號碼 -->20140420的3D號碼是:127, 我要記下來。
observer2 獲得 3D 號碼 -->20140420的3D號碼是:127我要告訴室友們。
observer1 獲得 3D 號碼 -->20140421的3D號碼是:333, 我要記下來。
observer2 獲得 3D 號碼 -->20140421的3D號碼是:333我要告訴室友們。

整個架構呈現鬆耦合:主題不依賴觀察者實現,新增觀察者無需修改主題代碼;觀察者如何處理數據與主題無關。

JDK 內置觀察者模式

Java 提供 java.util.Observable(類)與 java.util.Observer(接口)實現觀察者模式,代碼更簡潔。

1. 多主題示例:3D 與雙色球

package com.zhy.pattern.observer.java;
import java.util.Observable;

public class SubjectFor3d extends Observable {
    private String msg;
    public String getMsg() { return msg; }
    public void setMsg(String msg) {
        this.msg = msg;
        setChanged();
        notifyObservers();
    }
}

public class SubjectForSSQ extends Observable {
    private String msg;
    public String getMsg() { return msg; }
    public void setMsg(String msg) {
        this.msg = msg;
        setChanged();
        notifyObservers();
    }
}

2. 統一觀察者

package com.zhy.pattern.observer.java;
import java.util.Observable;
import java.util.Observer;

public class Observer1 implements Observer {
    public void registerSubject(Observable observable) {
        observable.addObserver(this);
    }
    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof SubjectFor3d) {
            System.out.println("subjectFor3d's msg -- >" + ((SubjectFor3d)o).getMsg());
        } else if (o instanceof SubjectForSSQ) {
            System.out.println("subjectForSSQ's msg -- >" + ((SubjectForSSQ)o).getMsg());
        }
    }
}

3. 測試

public class Test {
    public static void main(String[] args) {
        SubjectFor3d subjectFor3d = new SubjectFor3d();
        SubjectForSSQ subjectForSSQ = new SubjectForSSQ();
        Observer1 observer1 = new Observer1();
        observer1.registerSubject(subjectFor3d);
        observer1.registerSubject(subjectForSSQ);
        subjectFor3d.setMsg("hello 3d'nums : 110 ");
        subjectForSSQ.setMsg("ssq'nums : 12,13,31,5,4,3 15");
    }
}

4. 輸出

subjectFor3d's msg -- >hello 3d'nums : 110
subjectForSSQ's msg -- >ssq'nums : 12,13,31,5,4,3 15

關於 JDK 內置實現的思考

JDK 將 addObserver、removeObserver、notifyObservers 實現在 Observable 類中,而非接口。雖然違反「面向接口編程」,但避免了每個主題重複實現這三個方法,實現了代碼復用。若用接口則無法復用方法實現,也難以用組合方式復用。因此這種設計是合理的權衡。

總結

觀察者模式實現了主題與觀察者的解耦,適用於一對多的通知場景。無論自定義實現還是使用 JDK 內置類,核心思想一致:發布者不關心誰訂閱,訂閱者只關心感興趣的事件。掌握此模式,能靈活應對事件驅動、消息推送等業務場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved