Наблюдатель (шаблон проектирования)
| Наблюдатель | |
| Observer | |
| Тип: |
поведенческий |
|---|---|
| Назначение: |
|
| Описан в Design Patterns |
Да |
Шаблон Наблюдатель(англ. observer) — поведенческий шаблон проектирования. Также известен как «подчинённые» (Dependents), «издатель-подписчик» (Publisher-Subscriber). Создает механизм у класса, который позволяет получать оповещения от других класса об изменении их состояния, тем самым наблюдая за ними[2].
Содержание |
Назначение[править]
Определяет зависимость типа «один ко многим» между объектами таким образом, что при изменении состояния одного объекта все зависящие от него оповещаются об этом событии.
Реализация[править]
При реализации шаблона «наблюдатель» обычно используются следующие классы:
- Observable — интерфейс, определяющий методы для добавления, удаления и оповещения наблюдателей;
- Observer — интерфейс, с помощью которого наблюдатель получает оповещение;
- ConcreteObservable — конкретный класс, который реализует интерфейс Observable;
- ConcreteObserver — конкретный класс, который реализует интерфейс Observer.
Область применения[править]
Шаблон «наблюдатель» применяется в тех случаях, когда система обладает следующими свойствами:
- существует, как минимум, один объект, рассылающий сообщения;
- имеется не менее одного получателя сообщений, причём их количество и состав могут изменяться во время работы приложения;
- нет надобности очень сильно связывать взаимодействующие объекты, что полезно для повторного использования.
Данный шаблон часто применяют в ситуациях, в которых отправителя сообщений не интересует, что делают получатели с предоставленной им информацией.
Примеры[править]
PHP5 (SPL)[править]
/** * В 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[править]
interface 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#[править]
using System; using System.Collections; using System.Collections.Generic; using System.Threading; namespace Observer { /// <summary> /// Observer Pattern Judith Bishop Jan 2007 /// Updated by Kobel' Bohdan 2013 /// /// 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; } } interface ISubject { void AddObserver(IObserver observer); void RemoveObserver(IObserver observer); void NotifyObservers(string s); } class Subject : ISubject { public string SubjectState { get; set; } public List<IObserver> Observers { get; private set; } private Simulator simulator; private const int speed = 200; public Subject() { Observers = new List<IObserver>(); simulator = new Simulator(); } public void AddObserver(IObserver observer) { Observers.Add(observer); } public void RemoveObserver(IObserver observer) { Observers.Remove(observer); } public void NotifyObservers(string s) { foreach (var observer in Observers) { observer.Update(s); } } public void Go() { new Thread(new ThreadStart(Run)).Start( ); } void Run () { foreach (string s in simulator) { Console.WriteLine("Subject: " + s); SubjectState = s; NotifyObservers(s); Thread.Sleep(speed); // milliseconds } } } interface IObserver { void Update(string state); } class Observer : IObserver { string name; ISubject subject; string state; string gap; public Observer(ISubject subject, string name, string gap) { this.subject = subject; this.name = name; this.gap = gap; subject.AddObserver(this); } public void Update(string subjectState) { state = subjectState; Console.WriteLine(gap + name + ": " + state); } } }
Java[править]
import 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 Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); } interface DisplayElement { public void display(); } class WeatherData implements Subject { private ArrayList observers; private float temperature; private float humidity; private float pressure; public WeatherData() { observers = new ArrayList(); } @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 ( int i =0; i< observers.size(); i++) { Observer observer = (Observer) observers.get(i); observer.update(temperature, humidity, pressure); } } public 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 Subject weatherData; public CurrentConditionsDisplay(Subject 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++[править]
#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 { string _str; map<IObserver* const, IObserver* const> _observers; typedef 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 string& get() const { return _str; } void reset(string str) { _str = str; _Notify(); } }; class Reflector: public IObserver // Prints the observed string into cout { public: virtual void handleEvent(const SupervisedString& ref) { cout<<ref.get()<<endl; } }; class Counter: public IObserver // Prints the length of observed string into cout { virtual void handleEvent(const SupervisedString& ref) { cout << "length = " << ref.get().length() << endl; } }; int main() { SupervisedString str; Reflector refl; Counter cnt; str.add(refl); str.reset("Hello, World!"); cout << endl; str.remove(refl); str.add(cnt); str.reset("World, Hello!"); cout << 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; } } }
VB.NET[править]
Imports 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>[3].
Примечания[править]
- ↑ Паттерн Observer. Архивировано из первоисточника 14 июня 2013. Проверено 13 июня 2013.
- ↑ Шаблон разработки Observer. Архивировано из первоисточника 14 июня 2013. Проверено 13 июня 2013.
- ↑ Шаблон разработки Observer. Архивировано из первоисточника 14 июня 2013. Проверено 13 июня 2013.


