觀察者模式

📢 本文由 gemini-3-flash-preview 翻譯

Observer Pattern 物件行為型模式

意圖

定義物件間的一對多的相依關係,當一個物件的狀態發生改變時,所有依賴於它的物件都會得到通知並被自動更新。

結構

觀察者模式

其中:

  • Subject (目標) 知道它的觀察者,可以有任意多個觀察者觀察同一個目標;提供註冊和刪除觀察者物件的介面。
  • Observer (觀察者) 為那些在「目標發生改變時需獲得通知的物件」定義一個更新介面。
  • ConcreteSubject (具體目標) 將有關狀態存入各 ConcreteObserver 物件;當它的狀態發生改變時,向它的各個觀察者發出通知。
  • ConcreteObserver (具體觀察者) 維護一個指向 ConcreteSubject 物件的參照;儲存有關狀態,這些狀態應與目標的狀態保持一致;實作 Observer 的更新介面,以使自身狀態與目標的狀態保持一致。

適用性

Observer 模式適用於:

  • 當一個抽象模型有兩個面向,其中一個面向相依於另一個面向,將這兩者封裝在獨立的物件中以使它們可以各自獨立地改變和複用。
  • 當對一個物件的改變需要同時改變其他物件,而不知道具體有多少物件有待改變時。
  • 當一個物件必須通知其他物件,但它又不能假定其他物件是誰,即不希望這些物件是緊密耦合的。

例子 1

某檔案管理系統中定義了類別 OfficeDoc 和 DocExplorer。當類別 OfficeDoc 發生變化時,類別 DocExplorer 的所有物件都要更新其自身的狀態。現採用觀察者 (Observer) 設計模式來實作該需求,所設計的類別圖如下圖所示:

觀察者模式-例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import java.util.*;

interface Observer {
    public void update();
}

interface Subject {
    public void Attach(Observer obs);
    public void Detach(Observer obs);
    public void Notify();
    public void setStatus(int status);
    public int getStatus();
}

class OfficeDoc implements Subject {
    private List<Observer> myObs;
    private String mySubjectName;
    private int m_status;
    public OfficeDoc(String name) {
        mySubjectName = name;
        this.myObs = new ArrayList<Observer>();
        m_status = 0;
    }
    public void Attach(Observer obs) { this.myObs.add(obs); }
    public void Detach(Observer obs) { this.myObs.remove(obs); }
    public void Notify() {
        for (Observer obs : this.myObs) {obs.update(); }
    }
    public void setStatus(int status) {
        m_status = status;
        System.out.println("SetStatus subject[" + mySubjectName + "]status:" + status);
    }
    public int getStatus() { return m_status; }
}

class DocExplorer implements Observer {
    private String myObsName;
    public DocExplorer(String name,Subject sub) {
        myObsName = name;
        sub.Attach(this);
    }
    public void update() {
        System.out.println("update observer[" + myObsName + "]");
    }
}

class ObserverTest {
    public static void main(String[] args) {
        Subject subjectA = new OfficeDoc("subject A");
        Observer observerA = new DocExplorer("observer A", subjectA);
        subjectA.setStatus(1);
        subjectA.Notify();
    }
}

例子 2

Subject 為 Youtuber,Observer 為訂閱者。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import java.util.ArrayList;
import java.util.List;

public class ObserverPattern {
    public static void main(String[] args) {
        Subject subjectA = new ConcerteSubject();

        Observer observer1 = new ConcerteObserver("Mike", subjectA);
        Observer observer2 = new ConcerteObserver("Jane", subjectA);

        subjectA.Notify();
    }
}

interface Subject{ // 目標
    public void Attach(Observer observer);
    public void Detach(Observer observer);
    public void Notify();
    public String getState();
    public void setState(String state);
}

class ConcerteSubject implements Subject{
    private String state;

    private List<Observer> observerList;

    public String getState() {
        return state;
    }

    @Override
    public void setState(String state) {
        this.state = state;
        this.Notify();
    }

    public ConcerteSubject(){
        state = "-1";
        observerList = new ArrayList<>();
    }

    public void Attach(Observer observer){
        observerList.add(observer);
    }

    public void Detach(Observer observer){
        observerList.remove(observer);
    }

    public void Notify(){
        for(Observer o : observerList){
            o.update();
        }
    }
}

interface Observer{ // 觀察者
    public void update();
}

class ConcerteObserver implements Observer{
    private String name;
    private Subject subject;
    private String state;

    public ConcerteObserver(String name, Subject subject){
        this.name = name;
        this.state = subject.getState();
        this.subject = subject;

        subject.Attach(this);
    }

    @Override
    public void update(){
        System.out.println(this.name + " Received");
        this.state = subject.getState();
    }
}