觀察者模式

<image>

觀察者模式（observer pattern），是一種軟體設計模式。在此種模式中，一個目標物件管理所有相依於它的觀察者物件，並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用在即時事件處理系統。

## 結構

<image>

在上面的UML類圖中，`Subject`類不直接更新依賴對象的狀態，`Subject`轉而提及`Observer`接口（`update()`）來更新狀態，這使得`Subject`獨立於依賴對象的狀態是如何更新的。`Observer1`和`Observer2`通過將自己的同步於主題的狀態實現了`Observer`接口。

UML序列圖展示了運行時交互：`Observer1`和`Observer2`對象調用`attach(this)`於`Subject1`之上來註冊自身。假定`Subject1`的狀態變更了，`Subject1`調用`notify()`於自身之上。`notify()`調用`update()`在已註冊的`Observer1`和`Observer2`對象之上，它從`Subject1`要來變更了的數據（`getState()`）用於更新（同步）自身的狀態。

### 參與者

參與本模式的各類別列出如下。成員函式以模擬的方式列出。

抽象目標類別，提供一個界面讓觀察者進行添附與解附作業。此類別內有個不公開的觀察者串鍊，並透過下列函式(方法)進行作業：

- 添附(Attach)：新增觀察者到串鍊內，以追蹤目標物件的變化。
- 解附(Detach)：將已經存在的觀察者從串鍊中移除。
- 通知(Notify)：利用觀察者所提供的更新函式來通知此目標已經產生變化。

添附函式包涵了一個觀察者物件參數。也許是觀察者類別的虛擬函式(即更新函式)，或是在非物件導向的設定中所使用的函式指標(更廣泛來講，函式子或是函式物件)。

目標類別，提供了觀察者欲追蹤的狀態。也利用其源類別(例如前述的抽象目標類別)所提供的方法,來通知所有的觀察者其狀態已經更新。此類別擁有以下函式：

- 取得狀態(GetState)：回傳該目標物件的狀態。

抽象觀察者介面，是一個必須被實做的抽象類別。這個類別定義了所有觀察者都擁有的更新用介面，此介面是用來接收目標類別所發出的更新通知。此類別含有以下函式：

- 更新(Update)：會被實做的一個抽象(虛擬)函式。

抽象觀察者介面，含有指向目標類別的參考(reference)，以接收來自目標類別的更新狀態。此類別含有以下函式：

- 更新(Update)：是前述抽象函式的實做。當這個函式被目標物件呼叫時，觀察者物件將會呼叫目標物件的取得狀態函式，來其所擁有的更新目標物件資訊。

每個觀察者類別都要實做它自己的更新函式，以應對狀態更新的情形。

當目標物件改變時，會通過呼叫它自己的通知函式來將通知送給每一個觀察者物件，這個通知函式則會去呼叫已經添附在串鍊內的觀察者更新函式。通知與更新函式可能會有一些參數，好指明是目前目標物件內的何種改變。這麼作將可增進觀察者的效率(只更新那些改變部份的狀態)。

## 用途

- 當抽象個體有兩個互相依賴的層面時。封裝這些層面在單獨的物件內將可允許程式設計師單獨地去變更與重複使用這些物件，而不會產生兩者之間交互的問題。
- 當其中一個物件的變更會影響其他物件，卻又不知道多少物件必須被同時變更時。
- 當物件應該有能力通知其他物件，又不應該知道其他物件的實做細節時。

觀察者模式通常與MVC範式有關係。在MVC中，觀察者模式被用來降低model與view的耦合程度。一般而言，model的改變會觸發通知其他身為觀察者的model。而這些model實際上是view。Java Swing就是個範例，示意了model 預期會透過PropertyChangeNotification架構以送出改變的通知給其他view。Model類別是Java bean類別的一員，並擁有與上述目標類別同樣的行為。View類別則繫結了一些GUI中的可視元素，並擁有與上述觀察者類別同樣的行為。當應用程式在執行時。使用者將因view做出相應的更新而看見model所產生的變更。

## 示例

### C++

下面是C++23實現：

```c++
import std;

using std::reference_wrapper;
using std::vector;

class Subject; // Forward declaration for usage in Observer

class Observer {
private:
    // Reference to a Subject object to detach in the destructor
    Subject& subject;
public:
    explicit Observer(Subject& subj):     
        subject{subj} {
        subject.attach(*this);
    }

    virtual ~Observer() {
        subject.detach(*this);
    }
  
    Observer(const Observer&) = delete;
    Observer& operator=(const Observer&) = delete;

    virtual void update(Subject& s) const = 0;
};

// Subject is the base class for event generation
class Subject {
private:
    vector<RefObserver> observers;
public:
    using RefObserver = reference_wrapper<const Observer>;
  
    // Notify all the attached observers
    void notify() {
        for (const Observer& x: observers) {
            x.get().update(*this);
        }
    }
  
    // Add an observer
    void attach(const Observer& observer) {
        observers.push_back(observer);
    }
  
    // Remove an observer
    void detach(Observer& observer) {
        observers.remove_if([&observer](const RefObserver& obj) -> bool { 
            return &obj.get() == &observer; 
        });
    }
};

// Example of usage
class ConcreteObserver: public Observer {
public:
    explicit ConcreteObserver(Subject& subj): 
        Observer(subj) {}
  
    // Get notification
    void update(Subject&) const override {
        std::println("Got a notification");
    }
};

int main(int argc, char* argv[]) {
    Subject cs;
    ConcreteObserver co1(cs);
    ConcreteObserver co2(cs);
    cs.notify();
}
```

程序的輸出為：

```output
Got a notification
Got a notification
```

### Java

下面的Java例子接受鍵盤輸入並將每個輸入行作為一個事件來處理。當在一個字串由`System.in`提供出來之時，方法`notifyObservers()`接著被調用，用來通知所有觀察者這個事件發生了，這採用了調用它們的更新方法的形式。

```java
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

interface Observer {
    void update(String event);
}
  
class EventSource {
    List<Observer> observers = new ArrayList<>();
  
    public void notifyObservers(String event) {
        observers.forEach(observer -> observer.update(event));
    }
  
    public void addObserver(Observer observer) {
        observers.add(observer);
    }
  
    public void scanSystemIn() {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
            notifyObservers(line);
        }
    }
}

public class ObserverDemo {
    public static void main(String[] args) {
        System.out.println("Enter Text: ");
        EventSource eventSource = new EventSource();
        
        eventSource.addObserver(event -> System.out.println("Received response: " + event));

        eventSource.scanSystemIn();
    }
}
```

### C#

C#提供了IObservable和IObserver接口，還有如何實現這個設計模式的文檔。

```csharp
class Payload {
    internal string Message { get; init; }
}

class Subject : IObservable<Payload> {
    private readonly List<IObserver<Payload>> _observers = new List<IObserver<Payload>>();

    IDisposable IObservable<Payload>.Subscribe(IObserver<Payload> observer) {         
        if (!_observers.Contains(observer)) {
            _observers.Add(observer);
        }

        return new Unsubscriber(observer, _observers);
    }

    internal void SendMessage(string message) {
        foreach (var observer in _observers) {
            observer.OnNext(new Payload { Message = message });
        }
    }
}

internal class Unsubscriber : IDisposable {
    private readonly IObserver<Payload> _observer;
    private readonly ICollection<IObserver<Payload>> _observers;

    internal Unsubscriber(
        IObserver<Payload> observer,
        ICollection<IObserver<Payload>> observers) {
        _observer = observer;
        _observers = observers;
    }

    void IDisposable.Dispose() {
        if (_observer != null && _observers.Contains(_observer)) {
            _observers.Remove(_observer);
        }
    }
}

internal class Observer : IObserver<Payload> {
    private string _message;

    public void OnCompleted() {
    }

    public void OnError(Exception error) {
    }

    public void OnNext(Payload value) {
        _message = value.Message;
    }

    internal IDisposable Register(IObservable<Payload> subject) {
        return subject.Subscribe(this);
    }
}
```

### Python

下面是Python的例子：

```python
from abc import ABC, abstractmethod
from weakref import WeakValueDictionary

class Subject():
    def __init__(self):
        self.__observer_dict = WeakValueDictionary()
    def register(self, observer):
        self.__observer_dict[id(observer)] = observer
    def unregister(self, observer):
        del self.__observer_dict[id(observer)]
    def notify(self, *args, **kwargs):
        for key in self.__observer_dict:
            self.__observer_dict[key].update(self, *args, **kwargs)

class Observer(ABC):
    def __init__(self, subject):
        subject.register(self)
    @abstractmethod
    def update(self): pass

class ObserverA(Observer):
    def update(self, subject, *args, **kwargs):
        print("Got", args, kwargs, "From", subject)
```

其執行：

```pycon
>>> subject = Subject()
>>> observer1 = ObserverA(subject)
>>> observer2 = ObserverA(subject)
>>> subject.notify("test", kw="text")
Got ('test',) {'kw': 'text'} From <__main__.Subject object at 0x725f4bc71d30>
Got ('test',) {'kw': 'text'} From <__main__.Subject object at 0x725f4bc71d30>
```