- Наблюдатель (шаблон проектирования)
-
Шаблон проектирования Наблюдатель Observer Тип: поведенческий
Описан в Design Patterns Да
Наблюдатель, Observer — поведенческий шаблон проектирования. Также известен как «подчинённые» (Dependents), «издатель-подписчик» (Publisher-Subscriber).
Содержание
Назначение
Определяет зависимость типа «один ко многим» между объектами таким образом, что при изменении состояния одного объекта все зависящие от него оповещаются об этом событии.
Реализация
При реализации шаблона «наблюдатель» обычно используются следующие классы.
- Observable — интерфейс, определяющий методы для добавления, удаления и оповещения наблюдателей.
- Observer — интерфейс, с помощью которого наблюдатель получает оповещение.
- ConcreteObservable — конкретный класс, который реализует интерфейс Observable.
- ConcreteObserver — конкретный класс, который реализует интерфейс Observer.
Область применения
Шаблон «наблюдатель» применяется в тех случаях, когда система обладает следующими свойствами:
- существует, как минимум, один объект, рассылающий сообщения;
- имеется не менее одного получателя сообщений, причём их количество и состав могут изменяться во время работы приложения;
- нет надобности очень сильно связывать взаимодействующие объекты, что полезно для повторного использования.
Данный шаблон часто применяют в ситуациях, в которых отправителя сообщений не интересует, что делают получатели с предоставленной им информацией.
Примеры
Python
Исходный текст на языке pythonclass Subject(object): """Субъект""" def __init__(self): self.observers = set() def register(self, observer): """Регистрирует наблюдателя""" self.observers.add(observer) def unregister(self, observer): """Отменяет регистрацию наблюдателя""" self.observers.remove(observer) def notify(self, msg): """Оповещает наблюдателей""" for observer in self.observers: observer.update(self, msg) class Observer(object): """Наблюдатель""" def __init__(self, name): self.name = name def update(self, subject, msg): print ("[Observer {0}]: `{1}`".format(self.name, msg)) tony = Observer('Tony') angela = Observer('Angela') subject = Subject() subject.register(tony) subject.register(angela) subject.notify('Hello world!') # [Observer Tony]: `Hello world!` # [Observer Angela]: `Hello world!`
PHP5 (SPL)
Исходный текст на языке php/** * В PHP осуществляется встроенная поддержка этого шаблона через входящее в поставку * расширение SPL (Standard PHP Library): * SplObserver - интерфейс для Observer (наблюдателя), * SplSubject - интерфейс Observable (наблюдаемого), * SplObjectStorage - вспомогательный класс (обеспечивает улучшенное сохранение и удаление * объектов, в частности, реализованы методы attach() и detach()). */ class Observable implements SplSubject { private $storage; function __construct() { $this->storage = new SplObjectStorage(); } function attach(SplObserver $observer) { $this->storage->attach($observer); } function detach(SplObserver $observer) { $this->storage->detach($observer); } function notify() { foreach($this->storage as $obj) { $obj->update($this); } } //... } abstract class Observer implements SplObserver { private $observable; function __construct(Observable $observable) { $this->observable = $observable; $observable->attach($this); } function update(SplSubject $subject) { if($subject === $this->observable) { $this->doUpdate($subject); } } abstract function doUpdate(Observable $observable); } class ConcreteObserver extends Observer { function doUpdate(Observable $observable) { //... } } $observable = new Observable(); new ConcreteObserver($observable);
PHP5
Исходный текст на языке phpinterface Observer{ function notify($obj); } class ExchangeRate{ static private $instance = NULL; private $observers = array(); private $exchange_rate; private function __construct(){} private function __clone(){} static public function getInstance(){ if(self::$instance == NULL){ self::$instance = new ExchangeRate(); } return self::$instance; } public function getExchangeRate(){ return $this->exchange_rate; } public function setExchangeRate($new_rate){ $this->exchange_rate = $new_rate; $this->notifyObservers(); } public function registerObserver(Observer $obj){ $this->observers[] = $obj; } function notifyObservers(){ foreach($this->observers as $obj){ $obj->notify($this); } } } class ProductItem implements Observer{ public function __construct(){ ExchangeRate::getInstance()->registerObserver($this); } public function notify($obj){ if($obj instanceof ExchangeRate) { // Update exchange rate data print "Received update!\n"; } } } $product1 = new ProductItem(); $product2 = new ProductItem(); ExchangeRate::getInstance()->setExchangeRate(4.5);
C#
Исходный текст на языке csharpusing System; namespace Observer { /// <summary> /// Observer Pattern Judith Bishop Jan 2007 /// /// The Subject runs in a thread and changes its state /// independently. At each change, it notifies its Observers. /// </summary> class Program { static void Main(string[] args) { Subject subject = new Subject(); Observer Observer = new Observer(subject,"Center","\t\t"); Observer observer2 = new Observer(subject,"Right","\t\t\t\t"); subject.Go(); // Wait for user Console.Read(); } } class Simulator : IEnumerable { string [] moves = {"5","3","1","6","7"}; public IEnumerator GetEnumerator() { foreach( string element in moves ) yield return element; } } class Subject { public delegate void Callback (string s); public event Callback Notify; Simulator simulator = new Simulator( ); const int speed = 200; public string SubjectState { get; set; } public void Go() { new Thread(new ThreadStart(Run)).Start( ); } void Run () { foreach (string s in simulator) { Console.WriteLine("Subject: " + s); SubjectState = s; Notify(s); Thread.Sleep(speed); // milliseconds } } } interface IObserver { void Update(string state); } class Observer : IObserver { string name; Subject subject; string state; string gap; public Observer(Subject subject, string name, string gap) { this.subject = subject; this.name = name; this.gap = gap; subject.Notify += Update; } public void Update(string subjectState) { state = subjectState; Console.WriteLine(gap + name + ": " + state); } } }
Java
Исходный текст на языке Javaimport java.util.ArrayList; public class WeatherStation { public static void main(String[] args){ WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData); weatherData.setMeasurements(29, 65, 30.4f); weatherData.setMeasurements(39, 70, 29.4f); weatherData.setMeasurements(42, 72, 31.4f); } } interface Observer { public void update (float temperature, float humidity, float pressure); } interface Observable { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); } interface DisplayElement { public void display(); } class WeatherData implements Observable { private ArrayList<Observer> observers = new ArrayList<Observer>(); private float temperature; private float humidity; private float pressure; public WeatherData() { } @Override public void registerObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { int i = observers.indexOf(o); if (i>=0) observers.remove(i); } @Override public void notifyObservers() { for (Observer observer : observers){ observer.update(temperature, humidity, pressure); } } private void measurementsChanged() { notifyObservers(); } public void setMeasurements(float temperature, float humidity, float pressure){ this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); }; } class CurrentConditionsDisplay implements Observer, DisplayElement{ private float temperature; private float humidity; private Observable weatherData; public CurrentConditionsDisplay(Observable weatherData){ this.weatherData = weatherData; weatherData.registerObserver(this); } @Override public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; display(); } @Override public void display() { System.out.printf("Сейчас значения: %.1f градусов цельсия и %.1f %% влажности\n", temperature, humidity); } }
C++
Исходный текст на языке c++#include <iostream> #include <string> #include <map> #include <boost/foreach.hpp> using namespace std; class SupervisedString; class IObserver{ public: virtual void handleEvent(const SupervisedString&) = 0; }; class SupervisedString{ // Observable class std::string _str; std::map<IObserver* const, IObserver* const> _observers; typedef std::map<IObserver* const, IObserver* const>::value_type item; void _Notify(){ BOOST_FOREACH(item iter, _observers){ iter.second->handleEvent(*this); } } public: void add(IObserver& ref){ _observers.insert(item(&ref, &ref)); } void remove(IObserver& ref){ _observers.erase(&ref); } const std::string& get() const{ return _str; } void reset(std::string str){ _str = str; _Notify(); } }; class Reflector: public IObserver{ // Prints the observed string into std::cout public: virtual void handleEvent(const SupervisedString& ref){ std::cout<<ref.get()<<std::endl; } }; class Counter: public IObserver{ // Prints the length of observed string into std::cout virtual void handleEvent(const SupervisedString& ref){ std::cout<<"length = "<<ref.get().length()<<std::endl; } }; int main(){ SupervisedString str; Reflector refl; Counter cnt; str.add(refl); str.reset("Hello, World!"); std::cout<<std::endl; str.remove(refl); str.add (cnt); str.reset("World, Hello!"); std::cout<<std::endl; return 0; }
Delphi
Исходный текст на языке Delphiprogram Observer_pattern; {$APPTYPE CONSOLE} uses Classes; type IObserver = interface ['{9DB8219D-7A44-4226-B5A5-6A38C263F917}'] procedure HandleEvent; end; TConcreteObserver = class(TInterfacedObject, IObserver) public procedure HandleEvent; end; procedure TConcreteObserver.HandleEvent; begin Writeln('HandleEvent occurred'); end; type IObservable = interface ['{C7EA1E1B-E512-47EF-8FE2-D6B9F34BD935}'] procedure AddObserver(o: IObserver); procedure RemoveObserver(o: IObserver); procedure NotifyObservers; end; TConcreteObservable = class(TInterfacedObject, IObservable) private FObservers: TInterfaceList; public constructor Create; destructor Destroy; override; procedure AddObserver(o: IObserver); procedure RemoveObserver(o: IObserver); procedure NotifyObservers; end; constructor TConcreteObservable.Create; begin FObservers := TInterfaceList.Create; end; destructor TConcreteObservable.Destroy; begin FObservers.Free; end; procedure TConcreteObservable.AddObserver(o: IObserver); begin FObservers.Add(o); end; procedure TConcreteObservable.RemoveObserver(o: IObserver); begin FObservers.Remove(o); end; procedure TConcreteObservable.NotifyObservers; var i: Integer; begin for i := 0 to FObservers.Count - 1 do (FObservers[i] as IObserver).HandleEvent; end; var Observer: IObserver; Observable: IObservable; begin Observer := TConcreteObserver.Create; Observable := TConcreteObservable.Create; Observable.AddObserver(Observer); Observable.NotifyObservers; Observable.RemoveObserver(Observer); end.
ActionScript
Исходный текст на языке ActionScript//файл IObserver.as package { public interface IObserver { function notify(obj:Object):void; } } //файл ExchangeRate.as package { public class ExchangeRate { private static var _instance:ExchangeRate = null; private var observers:Array = []; private var _exchangeRate:Object; public function ExchangeRate() { if (_instance == null) throw new Error('Model Singleton!'); } public static function getInstance():ExchangeRate { if (_instance == null) _instance = new ExchangeRate(); return _instance; } public function get exchangeRate():Object { return _exchangeRate; } public function set exchangeRate(value:Object):void { _exchangeRate = value; this.notifyObservers(); } public function registerObserver(value:IObserver):void { this.observers.push(value); } private function notifyObservers():void { for each (var observer:IObserver in this.observers) { observer.notify(this); } } } } //файл ProductItem.as package { public class ProductItem implements IObserver { public function ProductItem() { ExchangeRate.getInstance().registerObserver(this); } public function notify(value:Object):void { if (value is ExchangeRate) { var exchange:ExchangeRate = value as ExchangeRate; trace(exchange.exchangeRate); } } } } //файл Main.as package { import flash.display.Sprite; public class Main extends Sprite { public function Main():void { var item1:ProductItem = new ProductItem(); var item2:ProductItem = new ProductItem(); ExchangeRate.getInstance().exchangeRate = 3.5; } } }
VB.NET
Исходный текст на языке VB.NETImports System.Collections Imports System.Threading Namespace Observer ''' <summary> ''' Observer Pattern Judith Bishop Jan 2007 ''' ''' The Subject runs in a thread and changes its state ''' independently. At each change, it notifies its Observers. ''' </summary> Class Program Shared Sub Main() Dim subject As New Subject() Dim Observer As New Observer(subject, "Center", vbTab & vbTab) Dim observer2 As New Observer(subject, "Right", vbTab & vbTab & vbTab & vbTab) subject.Go() ' Wait for user Console.Read() End Sub End Class Class Simulator Implements IEnumerable Private moves As String() = {"5", "3", "1", "6", "7"} Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator Return moves.GetEnumerator ' // Yield End Function End Class Class Subject Public Delegate Sub Callback(ByVal s As String) Public Event Notify As Callback Private simulator As New Simulator() Private m_SubjectState As String Private Const speed As Integer = 200 Public Property SubjectState() As String Get Return m_SubjectState End Get Set(ByVal value As String) m_SubjectState = value End Set End Property Public Sub Go() Call (New Thread(New ThreadStart(AddressOf Run))).Start() End Sub Private Sub Run() For Each s As String In simulator Console.WriteLine("Subject: " & s) SubjectState = s RaiseEvent Notify(s) ' milliseconds Thread.Sleep(speed) Next End Sub End Class Interface IObserver Sub Update(ByVal state As String) End Interface Class Observer Implements IObserver Private name As String Private subject As Subject Private state As String Private gap As String Public Sub New(ByVal subject As Subject, ByVal name As String, ByVal gap As String) Me.subject = subject Me.name = name Me.gap = gap AddHandler subject.Notify, AddressOf Update End Sub Public Sub Update(ByVal subjectState As String) Implements IObserver.Update state = subjectState Console.WriteLine(gap & name & ": " & state) End Sub End Class End Namespace
Примечания
В платформе .NET Framework 4.0 шаблон разработки наблюдателя применяется путем реализации универсальных интерфейсов System.IObservable<T> и System.IObserver<T>. http://msdn.microsoft.com/ru-ru/library/ee850490.aspx
См. также
- Поток управления — англ. flow control
- Singleton - частный случай уникального, единственного в системе обозреваемого объекта
Ссылки
- Паттерн Оbserver (наблюдатель) — назначение, описание, особенности и реализация на С++.
Шаблоны проектирования Основные Порождающие Структурные Поведенческие Интерпретатор • Итератор • Команда • Наблюдатель • Посетитель • Посредник • Состояние • Стратегия • Хранитель • Цепочка обязанностей • Шаблонный метод
Блокировка с двойной проверкой • Однопоточное выполнение • Планировщик Поведенческие шаблоны проектирования Interpreter/Интерпретатор | Template Method/Шаблонный метод | Command/Команда | Chain of Responsibility/Цепочка обязанностей | Iterator/Итератор, Cursor | Mediator/Посредник | Memento/Хранитель, Token | Observer/Наблюдатель, Listener | State/Состояние | Strategy/Стратегия | Visitor/Посетитель | Event listener | Single-serving visitor | Hierarchical visitor |
Категория:- Шаблоны проектирования
Wikimedia Foundation. 2010.