// 发布者基类包含订阅管理代码和通知方法。 class EventManager is private field listeners: hash map of event types and listeners
method subscribe(eventType, listener) is listeners.add(eventType, listener)
method unsubscribe(eventType, listener) is listeners.remove(eventType, listener)
method notify(eventType, data) is foreach (listener in listeners.of(eventType)) do listener.update(data)
// 具体发布者包含一些订阅者感兴趣的实际业务逻辑。我们可以从发布者基类中扩 // 展出该类,但在实际情况下并不总能做到,因为具体发布者可能已经是子类了。 // 在这种情况下,你可用组合来修补订阅逻辑,就像我们在这里做的一样。 class Editor is public field events: EventManager private field file: File
publicinterfaceISubject { // Attach an observer to the subject. voidAttach(IObserver observer);
// Detach an observer from the subject. voidDetach(IObserver observer);
// Notify all observers about an event. voidNotify(); }
// The Subject owns some important state and notifies observers when the // state changes. publicclassSubject : ISubject { // For the sake of simplicity, the Subject's state, essential to all // subscribers, is stored in this variable. publicint State { get; set; } = -0;
// List of subscribers. In real life, the list of subscribers can be // stored more comprehensively (categorized by event type, etc.). private List<IObserver> _observers = new List<IObserver>();
// The subscription management methods. publicvoidAttach(IObserver observer) { Console.WriteLine("Subject: Attached an observer."); this._observers.Add(observer); }
publicvoidDetach(IObserver observer) { this._observers.Remove(observer); Console.WriteLine("Subject: Detached an observer."); }
// Trigger an update in each subscriber. publicvoidNotify() { Console.WriteLine("Subject: Notifying observers...");
foreach (var observer in _observers) { observer.Update(this); } }
// Usually, the subscription logic is only a fraction of what a Subject // can really do. Subjects commonly hold some important business logic, // that triggers a notification method whenever something important is // about to happen (or after it). publicvoidSomeBusinessLogic() { Console.WriteLine("\nSubject: I'm doing something important."); this.State = new Random().Next(0, 10);
Thread.Sleep(15);
Console.WriteLine("Subject: My state has just changed to: " + this.State); this.Notify(); } }
// Concrete Observers react to the updates issued by the Subject they had // been attached to. classConcreteObserverA : IObserver { publicvoidUpdate(ISubject subject) { if ((subject as Subject).State < 3) { Console.WriteLine("ConcreteObserverA: Reacted to the event."); } } }
classConcreteObserverB : IObserver { publicvoidUpdate(ISubject subject) { if ((subject as Subject).State == 0 || (subject as Subject).State >= 2) { Console.WriteLine("ConcreteObserverB: Reacted to the event."); } } } classProgram { staticvoidMain(string[] args) { // The client code. var subject = new Subject(); var observerA = new ConcreteObserverA(); subject.Attach(observerA);
var observerB = new ConcreteObserverB(); subject.Attach(observerB);
Subject: Attached an observer. Subject: Attached an observer.
Subject: I'm doing something important. Subject: My state has just changed to: 2 Subject: Notifying observers... ConcreteObserverA: Reacted to the event. ConcreteObserverB: Reacted to the event.
Subject: I'm doing something important. Subject: My state has just changed to: 1 Subject: Notifying observers... ConcreteObserverA: Reacted to the event. Subject: Detached an observer.
Subject: I'm doing something important. Subject: My state has just changed to: 5 Subject: Notifying observers...
在 C++ 中使用模式
复杂度: ★★☆
流行度: ★★★
使用示例: 观察者模式在 C++ 代码中很常见, 特别是在 GUI 组件中。 它提供了在不与其他对象所属类耦合的情况下对其事件做出反应的方式。
/** * Observer Design Pattern * * Intent: Lets you define a subscription mechanism to notify multiple objects * about any events that happen to the object they're observing. * * Note that there's a lot of different terms with similar meaning associated * with this pattern. Just remember that the Subject is also called the * Publisher and the Observer is often called the Subscriber and vice versa. * Also the verbs "observe", "listen" or "track" usually mean the same thing. */
voidCreateMessage(std::string message = "Empty"){ this->message_ = message; Notify(); } voidHowManyObserver(){ std::cout << "There are " << list_observer_.size() << " observers in the list.\n"; }
/** * Usually, the subscription logic is only a fraction of what a Subject can * really do. Subjects commonly hold some important business logic, that * triggers a notification method whenever something important is about to * happen (or after it). */ voidSomeBusinessLogic(){ this->message_ = "change message message"; Notify(); std::cout << "I'm about to do some thing important\n"; }
Hi, I'm the Observer "1". Hi, I'm the Observer "2". Hi, I'm the Observer "3". There are 3 observers in the list. Observer "1": a new message is available --> Hello World! :D Observer "2": a new message is available --> Hello World! :D Observer "3": a new message is available --> Hello World! :D Observer "3" removed from the list. There are 2 observers in the list. Observer "1": a new message is available --> The weather is hot today! :p Observer "2": a new message is available --> The weather is hot today! :p Hi, I'm the Observer "4". Observer "2" removed from the list. Hi, I'm the Observer "5". There are 3 observers in the list. Observer "1": a new message is available --> My new car is great! ;) Observer "4": a new message is available --> My new car is great! ;) Observer "5": a new message is available --> My new car is great! ;) Observer "5" removed from the list. Observer "4" removed from the list. Observer "1" removed from the list. Goodbye, I was the Observer "5". Goodbye, I was the Observer "4". Goodbye, I was the Observer "3". Goodbye, I was the Observer "2". Goodbye, I was the Observer "1". Goodbye, I was the Subject.
Save to log \path\to\log\file.txt: Someone has performed open operation with the following file: test.txt Email to admin@example.com: Someone has performed save operation with the following file: test.txt
/** * PHP has a couple of built-in interfaces related to the Observer pattern. * * Here's what the Subject interface looks like: * * @link http://php.net/manual/en/class.splsubject.php * * interface SplSubject * { * // Attach an observer to the subject. * public function attach(SplObserver $observer); * * // Detach an observer from the subject. * public function detach(SplObserver $observer); * * // Notify all observers about an event. * public function notify(); * } * * There's also a built-in interface for Observers: * * @link http://php.net/manual/en/class.splobserver.php * * interface SplObserver * { * public function update(SplSubject $subject); * } */
/** * The Subject owns some important state and notifies observers when the state * changes. */ classSubjectimplements \SplSubject { /** * @var int For the sake of simplicity, the Subject's state, essential to * all subscribers, is stored in this variable. */ public$state;
/** * @var \SplObjectStorage List of subscribers. In real life, the list of * subscribers can be stored more comprehensively (categorized by event * type, etc.). */ private$observers;
/** * The subscription management methods. */ publicfunctionattach(\SplObserver$observer): void { echo"Subject: Attached an observer.\n"; $this->observers->attach($observer); }
publicfunctiondetach(\SplObserver$observer): void { $this->observers->detach($observer); echo"Subject: Detached an observer.\n"; }
/** * Trigger an update in each subscriber. */ publicfunctionnotify(): void { echo"Subject: Notifying observers...\n"; foreach ($this->observers as$observer) { $observer->update($this); } }
/** * Usually, the subscription logic is only a fraction of what a Subject can * really do. Subjects commonly hold some important business logic, that * triggers a notification method whenever something important is about to * happen (or after it). */ publicfunctionsomeBusinessLogic(): void { echo"\nSubject: I'm doing something important.\n"; $this->state = rand(0, 10);
echo"Subject: My state has just changed to: {$this->state}\n"; $this->notify(); } }
/** * Concrete Observers react to the updates issued by the Subject they had been * attached to. */ classConcreteObserverAimplements \SplObserver { publicfunctionupdate(\SplSubject$subject): void { if ($subject->state < 3) { echo"ConcreteObserverA: Reacted to the event.\n"; } } }
classConcreteObserverBimplements \SplObserver { publicfunctionupdate(\SplSubject$subject): void { if ($subject->state == 0 || $subject->state >= 2) { echo"ConcreteObserverB: Reacted to the event.\n"; } } }
Subject: Attached an observer. Subject: Attached an observer.
Subject: I'm doing something important. Subject: My state has just changed to: 2 Subject: Notifying observers... ConcreteObserverA: Reacted to the event. ConcreteObserverB: Reacted to the event.
Subject: I'm doing something important. Subject: My state has just changed to: 4 Subject: Notifying observers... ConcreteObserverB: Reacted to the event. Subject: Detached an observer.
Subject: I'm doing something important. Subject: My state has just changed to: 1 Subject: Notifying observers... ConcreteObserverA: Reacted to the event.
/** * The UserRepository represents a Subject. Various objects are interested in * tracking its internal state, whether it's adding a new user or removing one. */ classUserRepositoryimplements \SplSubject { /** * @var array The list of users. */ private$users = [];
// Here goes the actual Observer management infrastructure. Note that it's // not everything that our class is responsible for. Its primary business // logic is listed below these methods.
/** * @var array */ private$observers = [];
publicfunction__construct() { // A special event group for observers that want to listen to all // events. $this->observers["*"] = []; }
// Here are the methods representing the business logic of the class.
publicfunctioninitialize($filename): void { echo"UserRepository: Loading user records from a file.\n"; // ... $this->notify("users:init", $filename); }
publicfunctioncreateUser(array$data): User { echo"UserRepository: Creating a user.\n";
echo"Logger: I've written '$event' entry to the log.\n"; } }
/** * This Concrete Component sends initial instructions to new users. The client * is responsible for attaching this component to a proper user creation event. */ classOnboardingNotificationimplements \SplObserver { private$adminEmail;
UserRepository: Loading user records from a file. UserRepository: Broadcasting the 'users:init' event. Logger: I've written 'users:init' entry to the log. UserRepository: Creating a user. UserRepository: Broadcasting the 'users:created' event. OnboardingNotification: The notification has been emailed! Logger: I've written 'users:created' entry to the log. UserRepository: Deleting a user. UserRepository: Broadcasting the 'users:deleted' event. Logger: I've written 'users:deleted' entry to the log.
from __future__ import annotations from abc import ABC, abstractmethod from random import randrange from typing importList
classSubject(ABC): """ The Subject interface declares a set of methods for managing subscribers. """
@abstractmethod defattach(self, observer: Observer) -> None: """ Attach an observer to the subject. """ pass
@abstractmethod defdetach(self, observer: Observer) -> None: """ Detach an observer from the subject. """ pass
@abstractmethod defnotify(self) -> None: """ Notify all observers about an event. """ pass
classConcreteSubject(Subject): """ The Subject owns some important state and notifies observers when the state changes. """
_state: int = None """ For the sake of simplicity, the Subject's state, essential to all subscribers, is stored in this variable. """
_observers: List[Observer] = [] """ List of subscribers. In real life, the list of subscribers can be stored more comprehensively (categorized by event type, etc.). """
defattach(self, observer: Observer) -> None: print("Subject: Attached an observer.") self._observers.append(observer)
defnotify(self) -> None: """ Trigger an update in each subscriber. """
print("Subject: Notifying observers...") for observer in self._observers: observer.update(self)
defsome_business_logic(self) -> None: """ Usually, the subscription logic is only a fraction of what a Subject can really do. Subjects commonly hold some important business logic, that triggers a notification method whenever something important is about to happen (or after it). """
Subject: Attached an observer. Subject: Attached an observer.
Subject: I'm doing something important. Subject: My state has just changed to: 0 Subject: Notifying observers... ConcreteObserverA: Reacted to the event ConcreteObserverB: Reacted to the event
Subject: I'm doing something important. Subject: My state has just changed to: 5 Subject: Notifying observers... ConcreteObserverB: Reacted to the event
Subject: I'm doing something important. Subject: My state has just changed to: 0 Subject: Notifying observers... ConcreteObserverB: Reacted to the event
# The Subject interface declares a set of methods for managing subscribers. classSubject # Attach an observer to the subject. defattach(observer) raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
# Detach an observer from the subject. defdetach(observer) raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
# Notify all observers about an event. defnotify raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end end
# The Subject owns some important state and notifies observers when the state # changes. classConcreteSubject < Subject # For the sake of simplicity, the Subject's state, essential to all # subscribers, is stored in this variable. attr_accessor:state
# List of subscribers. In real life, the list of subscribers can be stored # more comprehensively (categorized by event type, etc.).
# @param [Obserser] observer defattach(observer) puts 'Subject: Attached an observer.' @observers << observer end
# @param [Obserser] observer defdetach(observer) @observers.delete(observer) end
# The subscription management methods.
# Trigger an update in each subscriber. defnotify puts 'Subject: Notifying observers...' @observers.each { |observer| observer.update(self) } end
# Usually, the subscription logic is only a fraction of what a Subject can # really do. Subjects commonly hold some important business logic, that # triggers a notification method whenever something important is about to # happen (or after it). defsome_business_logic puts "\nSubject: I'm doing something important." @state = rand(0..10)
puts "Subject: My state has just changed to: #{@state}" notify end end
# The Observer interface declares the update method, used by subjects. classObserver # Receive update from subject. defupdate(_subject) raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end end
# Concrete Observers react to the updates issued by the Subject they had been # attached to.
classConcreteObserverA < Observer # @param [Subject] subject defupdate(subject) puts 'ConcreteObserverA: Reacted to the event'if subject.state < 3 end end
Subject: Attached an observer. Subject: Attached an observer.
Subject: I'm doing something important. Subject: My state has just changed to: 2 Subject: Notifying observers... ConcreteObserverA: Reacted to the event ConcreteObserverB: Reacted to the event
Subject: I'm doing something important. Subject: My state has just changed to: 10 Subject: Notifying observers... ConcreteObserverB: Reacted to the event
Subject: I'm doing something important. Subject: My state has just changed to: 2 Subject: Notifying observers... ConcreteObserverB: Reacted to the event
在 Swift 中使用模式
复杂度: ★★☆
流行度: ★★★
使用示例: 观察者模式在 Swift 代码中很常见, 特别是在 GUI 组件中。 它提供了在不与其他对象所属类耦合的情况下对其事件做出反应的方式。
/// The Subject owns some important state and notifies observers when the state /// changes. classSubject {
/// For the sake of simplicity, the Subject's state, essential to all /// subscribers, is stored in this variable. var state: Int= { returnInt(arc4random_uniform(10)) }()
/// @var array List of subscribers. In real life, the list of subscribers /// can be stored more comprehensively (categorized by event type, etc.). privatelazyvar observers = [Observer]()
/// The subscription management methods. funcattach(_observer: Observer) { print("Subject: Attached an observer.\n") observers.append(observer) }
/// Trigger an update in each subscriber. funcnotify() { print("Subject: Notifying observers...\n") observers.forEach({ $0.update(subject: self)}) }
/// Usually, the subscription logic is only a fraction of what a Subject can /// really do. Subjects commonly hold some important business logic, that /// triggers a notification method whenever something important is about to /// happen (or after it). funcsomeBusinessLogic() { print("\nSubject: I'm doing something important.\n") state =Int(arc4random_uniform(10)) print("Subject: My state has just changed to: \(state)\n") notify() } }
/// The Observer protocol declares the update method, used by subjects. protocolObserver: class {
funcupdate(subject: Subject) }
/// Concrete Observers react to the updates issued by the Subject they had been /// attached to. classConcreteObserverA: Observer {
funcupdate(subject: Subject) {
if subject.state <3 { print("ConcreteObserverA: Reacted to the event.\n") } } }
classConcreteObserverB: Observer {
funcupdate(subject: Subject) {
if subject.state >=3 { print("ConcreteObserverB: Reacted to the event.\n") } } }
/// Let's see how it all works together. classObserverConceptual: XCTestCase {
functestObserverConceptual() {
let subject =Subject()
let observer1 =ConcreteObserverA() let observer2 =ConcreteObserverB()
CartManager: I'am adding a new subscriber: UINavigationBar CartManager: I'am adding a new subscriber: CartViewController
CartManager: I'am adding a new product: Apple UINavigationBar: Updating an appearance of navigation items CartViewController: Updating an appearance of a list view with products
CartManager: I'am adding a new product: T-shirt UINavigationBar: Updating an appearance of navigation items CartViewController: Updating an appearance of a list view with products
CartManager: Product 'Apple' is removed from a cart UINavigationBar: Updating an appearance of navigation items CartViewController: Updating an appearance of a list view with products
/** * The Subject interface declares a set of methods for managing subscribers. */ interfaceSubject { // Attach an observer to the subject. attach(observer: Observer): void;
// Detach an observer from the subject. detach(observer: Observer): void;
// Notify all observers about an event. notify(): void; }
/** * The Subject owns some important state and notifies observers when the state * changes. */ classConcreteSubjectimplementsSubject { /** * @type {number} For the sake of simplicity, the Subject's state, essential * to all subscribers, is stored in this variable. */ publicstate: number;
/** * @type {Observer[]} List of subscribers. In real life, the list of * subscribers can be stored more comprehensively (categorized by event * type, etc.). */ privateobservers: Observer[] = [];
/** * The subscription management methods. */ publicattach(observer: Observer): void { const isExist = this.observers.includes(observer); if (isExist) { returnconsole.log('Subject: Observer has been attached already.'); }
console.log('Subject: Attached an observer.'); this.observers.push(observer); }
this.observers.splice(observerIndex, 1); console.log('Subject: Detached an observer.'); }
/** * Trigger an update in each subscriber. */ publicnotify(): void { console.log('Subject: Notifying observers...'); for (const observer ofthis.observers) { observer.update(this); } }
/** * Usually, the subscription logic is only a fraction of what a Subject can * really do. Subjects commonly hold some important business logic, that * triggers a notification method whenever something important is about to * happen (or after it). */ publicsomeBusinessLogic(): void { console.log('\nSubject: I\'m doing something important.'); this.state = Math.floor(Math.random() * (10 + 1));
console.log(`Subject: My state has just changed to: ${this.state}`); this.notify(); } }
/** * The Observer interface declares the update method, used by subjects. */ interfaceObserver { // Receive update from subject. update(subject: Subject): void; }
/** * Concrete Observers react to the updates issued by the Subject they had been * attached to. */ classConcreteObserverAimplementsObserver { publicupdate(subject: Subject): void { if (subject instanceofConcreteSubject && subject.state < 3) { console.log('ConcreteObserverA: Reacted to the event.'); } } }
classConcreteObserverBimplementsObserver { publicupdate(subject: Subject): void { if (subject instanceofConcreteSubject && (subject.state === 0 || subject.state >= 2)) { console.log('ConcreteObserverB: Reacted to the event.'); } } }
Subject: Attached an observer. Subject: Attached an observer.
Subject: I'm doing something important. Subject: My state has just changed to: 6 Subject: Notifying observers... ConcreteObserverB: Reacted to the event.
Subject: I'm doing something important. Subject: My state has just changed to: 1 Subject: Notifying observers... ConcreteObserverA: Reacted to the event. Subject: Detached an observer.
Subject: I'm doing something important. Subject: My state has just changed to: 5 Subject: Notifying observers...
Item Nike Shirt is now in stock Sending email to customer abc@gmail.com for item Nike Shirt Sending email to customer xyz@gmail.com for item Nike Shirt