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

深入解析觀察者模式及其在 Java 設計模式開發中的應用

     編輯:Java 相關知識

觀察者模式簡介

觀察者模式,又稱發布-訂閱模式,是一種行為型設計模式。它定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一主題對象。當主題對象的狀態發生變化時,會自動通知所有已註冊的觀察者,使其能夠做出相應的反應。這種模式廣泛應用於事件處理系統、消息推送、GUI 程式設計等場景。

觀察者模式的核心角色

  • Subject(抽象主題介面):定義註冊、移除、通知觀察者的操作。
  • ConcreteSubject(具體主題類):實現主題介面,維護觀察者列表,狀態改變時發送通知。
  • Observer(抽象觀察者介面):定義更新介面,接收主題通知。
  • ConcreteObserver(具體觀察者類):實現更新邏輯,處理來自主題的通知。

自定義觀察者模式實現

以下展示一個支援多線程環境、觀察者可自主訂閱/取消訂閱的完整實現:

主題介面與實現

public interface Subject {
    void register(Observer observer);
    void remove(Observer observer);
    void notifyObservers();
    String getMessage();
}

public class MySubject implements Subject {
    private final List<Observer> observers = new ArrayList<>();
    private volatile boolean changed = false;
    private String message;
    private final Object mutex = new Object();

    @Override
    public void register(Observer observer) {
        if (observer == null) throw new NullPointerException();
        synchronized (mutex) {
            if (!observers.contains(observer)) observers.add(observer);
        }
    }

    @Override
    public void remove(Observer observer) {
        synchronized (mutex) {
            observers.remove(observer);
        }
    }

    @Override
    public void notifyObservers() {
        List<Observer> tempObservers;
        synchronized (mutex) {
            if (!changed) return;
            tempObservers = new ArrayList<>(observers);
            changed = false;
        }
        for (Observer obj : tempObservers) {
            obj.update();
        }
    }

    public void makeChanged(String message) {
        System.out.println("主題發生變更: " + message);
        this.message = message;
        this.changed = true;
        notifyObservers();
    }

    @Override
    public String getMessage() {
        return this.message;
    }
}

觀察者介面與實現

public interface Observer {
    void update();
    void setSubject(Subject subject);
}

public class MyObserver implements Observer {
    private Subject subject;

    @Override
    public void update() {
        String msg = subject.getMessage();
        System.out.println("觀察者收到通知: " + msg);
    }

    @Override
    public void setSubject(Subject subject) {
        this.subject = subject;
    }

    public void subscribe(String subjectName) {
        SubjectManagement.getInstance().getSubject(subjectName).register(this);
    }

    public void cancelSubscribe(String subjectName) {
        SubjectManagement.getInstance().getSubject(subjectName).remove(this);
    }
}

主題管理器(單例模式)

public class SubjectManagement {
    private final Map<String, Subject> subjectMap = new HashMap<>();

    public void addSubject(String name, Subject subject) {
        subjectMap.put(name, subject);
    }

    public Subject getSubject(String name) {
        return subjectMap.get(name);
    }

    private SubjectManagement() {}
    public static SubjectManagement getInstance() {
        return SubjectManagementHolder.INSTANCE;
    }
    private static class SubjectManagementHolder {
        static final SubjectManagement INSTANCE = new SubjectManagement();
    }
}

測試案例:作家與讀者

public class ObserverTest {
    private static MySubject writer;

    @BeforeClass
    public static void setUp() {
        writer = new MySubject();
        SubjectManagement.getInstance().addSubject("Linus", writer);
    }

    @Test
    public void testObserver() {
        MyObserver reader1 = new MyObserver();
        MyObserver reader2 = new MyObserver();
        MyObserver reader3 = new MyObserver();

        reader1.setSubject(writer);
        reader2.setSubject(writer);
        reader3.setSubject(writer);

        reader1.subscribe("Linus");
        reader2.subscribe("Linus");
        reader3.subscribe("Linus");

        writer.makeChanged("發布新文章:Java 設計模式深度解析");
    }
}

Servlet Listener 與事件驅動模型

Servlet 規範中的 Listener 正是觀察者模式的另一種形式——事件驅動模型的典型應用。JDK 提供了統一的事件源 EventObject 和標記介面 EventListener。以 ServletRequestAttributeListener 為例,當請求屬性發生變化時,容器會遍歷所有註冊的監聽器並調用對應方法:

private void notifyAttributeAssigned(String name, Object value, Object oldValue) {
    Object[] listeners = context.getApplicationEventListeners();
    if (listeners == null || listeners.length == 0) return;

    ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(...);
    for (Object listener : listeners) {
        if (listener instanceof ServletRequestAttributeListener) {
            ServletRequestAttributeListener l = (ServletRequestAttributeListener) listener;
            l.attributeAdded(event); // 或 attributeReplaced/Removed
        }
    }
}

JDK 內建觀察者模式:Observable/Observer

Java 標準庫提供 java.util.Observable 和 java.util.Observer,可直接用於快速實現。以下以「臥底接收行動指令」為例:

public class Police extends Observable {
    public void change(String time) {
        this.time = time;
        setChanged();
        notifyObservers(time);
    }
}

public class UndercoverA implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("臥底A收到指令,行動時間:" + arg);
    }
}

public class Client {
    public static void main(String[] args) {
        List<Observer> list = Arrays.asList(new UndercoverA(), new UndercoverB());
        Police police = new Police(list);
        police.change("02:25");
        police.change("01:05"); // 時間變更,自動通知所有觀察者
    }
}

優缺點總結

優點

  • 抽象主題僅依賴抽象觀察者,實現鬆耦合。
  • 支援廣播式通信,一對多通知。
  • 將資料產生層與回應層分離。

缺點

  • 觀察者過多時,通知所有觀察者耗時較長。
  • 若某觀察者處理阻塞,會影響其他觀察者的及時通知。
  • 可能產生循環引用導致記憶體洩漏。

核心實現要點

  1. 明確角色:區分主題與觀察者,建立一對多關係。
  2. 建立聯繫:主題內部維護觀察者集合,提供註冊/移除方法。
  3. 狀態變更通知:主題狀態改變時,遍歷集合調用觀察者的 update 方法。
  4. 觀察者註冊:觀察者構造時將自身註冊到主題,並持有主題引用以獲取狀態。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved