Posted onEdited onIndesign-patternViews: Symbols count in article: 4.4kReading time ≈16 mins.
也称为:Event-Subscriber、Listener
Intent 意图
观察者是一种行为设计模式,可用于定义订阅机制,以通知多个对象它们正在观察的对象发生的任何事件。
Problem 问题
假设您有两种类型的对象:a 假设您有两种类型的对象:a Customer 和 a 和 a 和 a Store 。客户对特定品牌的产品(例如,它是iPhone的新型号)非常感兴趣,该产品应该很快就会在商店中上市。 。客户对特定品牌的产品(例如,它是iPhone的新型号)非常感兴趣,该产品应该很快就会在商店中上市。
// The base publisher class includes subscription management // code and notification methods. classEventManager 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)
// The concrete publisher contains real business logic that's // interesting for some subscribers. We could derive this class // from the base publisher, but that isn't always possible in // real life because the concrete publisher might already be a // subclass. In this case, you can patch the subscription logic // in with composition, as we did here. classEditor is public field events: EventManager private field file: File
constructor Editor()is events=newEventManager()
// Methods of business logic can notify subscribers about // changes. method openFile(path) is this.file = newFile(path) events.notify("open", file.name)
method saveFile() is file.write() events.notify("save", file.name)
// ...
// Here's the subscriber interface. If your programming language // supports functional types, you can replace the whole // subscriber hierarchy with a set of functions. interfaceEventListener is method update(filename)
// Concrete subscribers react to updates issued by the publisher // they are attached to. classLoggingListenerimplementsEventListener is private field log: File private field message: string
constructor LoggingListener(log_filename, message) is this.log = newFile(log_filename) this.message = message
method update(filename) is log.write(replace('%s',filename,message))
classEmailAlertsListenerimplementsEventListener is private field email: string private field message: string
constructor EmailAlertsListener(email, message) is this.email = email this.message = message
method update(filename) is system.email(email, replace('%s',filename,message))
// An application can configure publishers and subscribers at // runtime. classApplication is method config()is editor=newEditor()
logger = newLoggingListener( "/path/to/log.txt", "Someone has opened the file: %s") editor.events.subscribe("open", logger)
emailAlerts = newEmailAlertsListener( "admin@example.com", "Someone has changed the file: %s") editor.events.subscribe("save", emailAlerts)
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
/// Editor has its own logic and it utilizes a publisher /// to operate with subscribers and events. #[derive(Default)] pubstructEditor { publisher: Publisher, file_path: String, }
fnsave_listener(file_path: String) { letemail = "admin@example.com".to_string(); println!("Email to {}: Save file {}", email, file_path); }
Output 输出
1 2 3
Save log to /path/to/log/file.txt: Load file test1.txt Save log to /path/to/log/file.txt: Load file test2.txt Email to admin@example.com: Save file test2.txt