Спостерігач (шаблон проектування)
Спостерігач, Observer — поведінковий шаблон проектування. Також відомий як «підлеглі» (Dependents), «видавець-передплатник» (Publisher-Subscriber).
Зміст |
Призначення [ред.]
Визначає залежність типу «один до багатьох» між об'єктами таким чином, що при зміні стану одного об'єкта всіх залежних від нього сповіщають про цю подію.
Устрій [ред.]
При реалізації шаблону «спостерігач» зазвичай використовуються такі класи:
- Observable — інтерфейс, що визначає методи для додавання, видалення та оповіщення спостерігачів.
- Observer — інтерфейс, за допомогою якого спостережуваний об'єкт оповіщає спостерігачів.
- ConcreteObservable — конкретний клас, який реалізує інтерфейс Observable.
- ConcreteObserver — конкретний клас, який реалізує інтерфейс Observer.
При зміні спостережуваного об'єкту, оповіщення спостерігачів може бути реалізоване за такими сценаріями:
- Спостережуваний об'єкт надсилає, кожному із зареєстрованих спостерігачів, всю потенційно релевантну інформацію (примусове розповсюдження).
- Спостережуваний об'єкт надсилає, кожному із зареєстрованих спостерігачів, лише повідомлення про те що інформація була змінена, а кожен із спостерігачів, за необхідності, самостійно здійснює запит необхідної інформації у спостережуваного об'єкта (розповсюдження за запитом).
Область застосування [ред.]
Шаблон «спостерігач» застосовується в тих випадках, коли система володіє такими властивостями:
- існує, як мінімум, один об'єкт, що розсилає повідомлення
- є не менше одного одержувача повідомлень, причому їхня кількість і склад можуть змінюватися під час роботи програми.
Цей шаблон часто застосовують в ситуаціях, в яких відправника повідомлень не цікавить, що роблять одержувачі з наданою їм інформацією.
Приклади [ред.]
Java [ред.]
package example.pattern.observer; import java.util.ArrayList; import java.util.List; public interface Subject { void attach(Observer o); void detach(Observer o); void notifyObserver(); } public interface Observer { void update(); } public class ConcreteSubject implements Subject { private List<Observer> observers = new ArrayList<Observer>(); private int value; public void setValue(int value) { this.value = value; notifyObserver(); } public int getValue() { return value; } @Override public void attach(Observer o) { observers.add(o); } @Override public void detach(Observer o) { observers.remove(o); } @Override public void notifyObserver() { for (Observer o : observers) { o.update(); } } } public class ConcreteObserver1 implements Observer { private ConcreteSubject subject; public ConcreteObserver1(ConcreteSubject subject) { this.subject = subject; } @Override public void update() { System.out.println("Observer1: " + subject.getValue()); } } public class ConcreteObserver2 implements Observer { private ConcreteSubject subject; public ConcreteObserver2(ConcreteSubject subject) { this.subject = subject; } @Override public void update() { String out = ""; for (int i = 0; i < subject.getValue(); i++) { out += "*"; } System.out.println("Observer2: " + out); } } public class Program { public static void main(String[] args) { ConcreteSubject subject = new ConcreteSubject(); ConcreteObserver1 observer1 = new ConcreteObserver1(subject); ConcreteObserver2 observer2 = new ConcreteObserver2(subject); subject.attach(observer1); subject.attach(observer2); subject.setValue(3); subject.setValue(8); } }
PHP5 [ред.]
<?php // Інтерфейс, за допомогою якого спостережуваний об'єкт сповіщає спостерігачів interface Observer{ function notify($obj); } // Клас спостережуваного об'єкта class ExchangeRate{ static private $instance = NULL; private $observers = array(); private $exchange_rate; private function ExchangeRate(){ } 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($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(); // Зміна стану спостережуваного об'єкта та автоматичне // оповіщення про це спостерігачів через функцію notify() ExchangeRate::getInstance()->setExchangeRate(4.5); ?>
C# [ред.]
using System; using System.Collections; using 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 { 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); } } }
C++ [ред.]
#include <iostream> #include <string> #include <map> #include <boost/foreach.hpp> 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; }
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; } } }
Реалізації [ред.]
Шаблон Спостерігач реалізований в численних бібліотеках і системах, включаючи майже всі інструментарії графічних інтерфейсів користувача.
Деякі з найпомітніших реалізацій шаблону перелічені нижче:
ActionScript [ред.]
- flash.events, пакет у ActionScript 3.0 (який наслідував пакет mx.events у ActionScript 2.0).
BASIC [ред.]
- Using the Observer Pattern, обговорення і реалізація в REALbasic
C [ред.]
- GObject, у GLib — реалізація об'єктів і сигналів/зворотних викликів (Callback) в C. (Ця бібліотека часто включена в інші мови програмування)
C++ [ред.]
- libsigc++ — бібліотека сигнальних шаблонів
- sigslot — C++ Signal/Slot Library
- Cpp::Events — Template-based C++ implementation that introduces separation of connection management interface of the event object from the invocation interface.
- XLObject — Template-based C++ signal/slot model patterned after Qt.
- Signals — A lightweight and non-intrusive C++ signal/slot model implementation.
- libevent — Multi-threaded Crossplatform Signal/Slot C++ Library
- Boost.Signals, an extension of the C++ STL providing a signal/slot model
- The Qt C++ framework's signal/slot model
C# [ред.]
- Exploring the Observer Design Pattern — the C# and Visual Basic .NET implementation, using delegates and the Event pattern
.NET Remoting, Applying the Observer Pattern in .NET Remoting (using C#)
Delphi [ред.]
- Delphi Observer Pattern, a Delphi implementation
Java [ред.]
- The Java Swing library makes extensive use of the observer pattern for event management
- PerfectJPattern Open Source Project, Provides a context-free and type-safe implementation of the Observer Pattern in Java.
JavaScript [ред.]
- EventDispatcher singleton, a JavaScript core API based Signals and slots implementation — an observer concept different from Publish/subscribe — pretty lightweighted but still type-safety enforcing.
Lisp [ред.]
- Cells, a dataflow extension to Common Lisp that uses meta-programming to hide some of the details of Observer pattern implementation.
PHP [ред.]
- Event_Dispatcher, a PHP implementation
- SPL, the Standard PHP Library
Python [ред.]
- Py-notify, a Python implementation
- Observer Pattern using Weak References implementation by Michael Kent
- PyPubSub an in-application Pub/Sub library for Observer behavior
- NotificationFramework classes directly implementing Observer patterns
Ruby [ред.]
- Observer, from the Ruby Standard Library. Also see Russ Olsen's coverage of this pattern in Ruby in Design Patterns in Ruby
Інше [ред.]
- CSP — Observer Pattern using CSP-like Rendezvous (each actor is a process, communication is via rendezvous).
- YUI Event utility implements custom events through the observer pattern
- Publish/Subscribe with LabVIEW, Implementation example of Observer or Publish/Subscribe using G.
Посилання [ред.]
- Observer Pattern implementation in JDK 6
- A sample implementation in .NET
- Observer Pattern in Java
- Definition, C# example & UML diagram
- Jt J2EE Pattern Oriented Framework
- Subject Observer example in C++
- Observer Pattern recipe in Python
Джерела [ред.]
Література [ред.]
Алан Шаллоуей, Джеймс Р. Тротт Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М.: «Вильямс», 2002. — 288 с. — ISBN 0-201-71594-5
|
||||||||||||||


