Паттерн «Наблюдатель» (Observer)
Назначение: определяет зависимость типа «один ко многим» между объектами таким образом, что при изменении состояния одного объекта все зависящие от него оповещаются об этом и автоматически обновляются.
Другими словами: наблюдатель уведомляет все заинтересованные стороны о произошедшем событии или об изменении своего состояния.
Когда использовать паттерн Наблюдатель?
-
Когда система состоит из множества классов, объекты которых должны находиться в согласованных состояниях
-
Когда общая схема взаимодействия объектов предполагает две стороны: одна рассылает сообщения и является главным, другая получает сообщения и реагирует на них. Отделение логики обеих сторон позволяет их рассматривать независимо и использовать отдельно друга от друга.
-
Когда существует один объект, рассылающий сообщения, и множество подписчиков, которые получают сообщения. При этом точное число подписчиков заранее неизвестно и процессе работы программы может изменяться.
UML схема паттерна Observer:
Реализация шаблона "наблюдатель" на .NET
using System;
using System.Collections.Generic;
namespace DoFactory.GangOfFour.Observer.Structural
{
/// <summary>
/// MainApp startup class for Structural
/// Observer Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
// Configure Observer pattern
ConcreteSubject s = new ConcreteSubject();
s.Attach(new ConcreteObserver(s, "X"));
s.Attach(new ConcreteObserver(s, "Y"));
s.Attach(new ConcreteObserver(s, "Z"));
// Change subject and notify observers
s.SubjectState = "ABC";
s.Notify();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Subject' abstract class
/// </summary>
abstract class Subject
{
private List<Observer> _observers = new List<Observer>();
public void Attach(Observer observer)
{
_observers.Add(observer);
}
public void Detach(Observer observer)
{
_observers.Remove(observer);
}
public void Notify()
{
foreach (Observer o in _observers)
{
o.Update();
}
}
}
/// <summary>
/// The 'ConcreteSubject' class
/// </summary>
class ConcreteSubject : Subject
{
private string _subjectState;
// Gets or sets subject state
public string SubjectState
{
get { return _subjectState; }
set { _subjectState = value; }
}
}
/// <summary>
/// The 'Observer' abstract class
/// </summary>
abstract class Observer
{
public abstract void Update();
}
/// <summary>
/// The 'ConcreteObserver' class
/// </summary>
class ConcreteObserver : Observer
{
private string _name;
private string _observerState;
private ConcreteSubject _subject;
// Constructor
public ConcreteObserver(
ConcreteSubject subject, string name)
{
this._subject = subject;
this._name = name;
}
public override void Update()
{
_observerState = _subject.SubjectState;
Console.WriteLine("Observer {0}'s new state is {1}",
_name, _observerState);
}
// Gets or sets subject
public ConcreteSubject Subject
{
get { return _subject; }
set { _subject = value; }
}
}
}
Схема примера использования паттерна "Наблюдатель" из реальной жизни:
Примеры в .NET Framework
- Наблюдатели в форме событий. В .NET Framework насчитываются тысячи классов, содержащих события.
- Наблюдатели в форме делегатов. Наблюдатели в форме делегатов часто используются в качестве методов обратного вызова для выполнения дополнительной инициализации (AppDomainSetup.AppDomainInitializer, HttpConfiguration.Initializer) или в качестве точек расширения (фильтры и селекторы в WPF/Windows Forms/WCF).
- Наблюдатели в форме интерфейсов. Хорошим примером именованного наблюдателя является API для работы с Event Hub — масштабируемой системой обмена сообщениями. Интерфейс IEventProcessor содержит методы CloseAsync, OpenAsync и ProcessEventsAsync.