热门问题
时间线
聊天
视角

观察者模式

設計模式 来自维基百科,自由的百科全书

观察者模式
Remove ads

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

Thumb
观察者模式的UML类图

參與類別

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

Thumb
观察者模式的样例UML类图和序列图[2]

抽象目標類別

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

  • 添附(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 所產生的變更。

Remove ads

示例

Java

下面的Java例子接受键盘输入并将每个输入行作为一个事件来处理。当在一个字符串由System.in提供出来之时,方法notifyObservers()接着被调用,用来通知所有观察者这个事件发生了,这采用了调用它们的更新方法的形式。

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();
    }
}
Remove ads

C#

C#提供了IObservable[3]IObserver[4]接口,还有如何实现这个设计模式的文档[5]

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

from abc import ABC, abstractmethod

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

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

class Subject():
    def __init__(self):
        self._observers = []
    def register_observer(self, observer):
        self._observers.append(observer)
    def unregister_observer(self, observer):
        self._observers.remove(observer)
    def notify_observers(self, *args, **kwargs):
        for observer in self._observers:
            observer.update(self, *args, **kwargs)

其执行:

>>> subject = Subject()
>>> observer = ObserverA(subject)
>>> subject.notify_observers("test", kw="python")
Got ('test',) {'kw': 'python'} From <__main__.Subject object at 0x736310a21940>
Remove ads

引用

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads