热门问题
时间线
聊天
视角
观察者模式
設計模式 来自维基百科,自由的百科全书
Remove ads
观察者模式(英语:observer pattern),是一种软体设计模式[1]。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用在即时事件处理系统。

结构

在上面的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):是前述抽象函式的实做。当这个函式被目标物件呼叫时,观察者物件将会呼叫目标物件的取得状态函式,来其所拥有的更新目标物件资讯。
每个观察者类别都要实做它自己的更新函式,以应对状态更新的情形。
当目标物件改变时,会通过呼叫它自己的通知函式来将通知送给每一个观察者物件,这个通知函式则会去呼叫已经添附在串炼内的观察者更新函式。通知与更新函式可能会有一些参数,好指明是目前目标物件内的何种改变。这么作将可增进观察者的效率(只更新那些改变部份的状态)。
Remove ads
用途
- 当抽象个体有两个互相依赖的层面时。封装这些层面在单独的物件内将可允许程式设计师单独地去变更与重复使用这些物件,而不会产生两者之间交互的问题。
- 当其中一个物件的变更会影响其他物件,却又不知道多少物件必须被同时变更时。
- 当物件应该有能力通知其他物件,又不应该知道其他物件的实做细节时。
观察者模式通常与MVC范式有关系。在MVC中,观察者模式被用来降低model与view的耦合程度。一般而言,model的改变会触发通知其他身为观察者的model。而这些model实际上是view。Java Swing就是个范例,示意了model 预期会透过PropertyChangeNotification架构以送出改变的通知给其他view。Model类别是Java bean类别的一员,并拥有与上述目标类别同样的行为。View类别则系结了一些GUI中的可视元素,并拥有与上述观察者类别同样的行为。当应用程式在执行时。使用者将因view做出相应的更新而看见model所产生的变更。
示例
下面是C++11实现:
#include <functional>
#include <iostream>
#include <list>
class Subject; //Forward declaration for usage in Observer
class Observer {
public:
explicit Observer(Subject& subj);
virtual ~Observer();
Observer(const Observer&) = delete; // rule of three
Observer& operator=(const Observer&) = delete;
virtual void update( Subject& s) const = 0;
private:
// Reference to a Subject object to detach in the destructor
Subject& subject;
};
// Subject is the base class for event generation
class Subject {
public:
using RefObserver = std::reference_wrapper<const Observer>;
// Notify all the attached observers
void notify() {
for (const auto& x: observers) {
x.get().update(*this);
}
}
// Add an observer
void attach(const Observer& observer) {
observers.push_front(observer);
}
// Remove an observer
void detach(Observer& observer) {
observers.remove_if(
[&observer](const RefObserver& obj) {
return &obj.get()==&observer;
});
}
private:
std::list<RefObserver> observers;
};
Observer::Observer(Subject& subj) : subject(subj) {
subject.attach(*this);
}
Observer::~Observer() {
subject.detach(*this);
}
// Example of usage
class ConcreteObserver: public Observer {
public:
ConcreteObserver(Subject& subj) : Observer(subj) {}
// Get notification
void update(Subject&) const override {
std::cout << "Got a notification" << std::endl;
}
};
int main()
{
Subject cs;
ConcreteObserver co1(cs);
ConcreteObserver co2(cs);
cs.notify();
}
程序的输出:
Got a notification
Got a notification
Remove ads
下面的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#提供了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);
}
}
Remove ads
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
引用
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads