Наблюдатель (Observer)

Шаблон Наблюдатель определяет отношение между объектами таким образом, что при изменении состояния одного объекта все зависящие от него объекты оповещаются об этом. Основное назначение шаблона Наблюдатель – реализация системы работы с событиями.

Разберём устройство шаблона Наблюдатель (рис. 13). Объект класса Subject – наблюдаемый объект – имеет некоторое состояние, изменение которого предполагается отслеживать. Для этого в классе Subject описывается коллекция объектов-наблюдателей и методы добавления и удаления элементов коллекции. Каждый объект-наблюдатель реализует интерфейс IObserver. При изменении состояния наблюдаемого объекта вызывается метод NotifyObservers(). Этот метод итерируется по коллекции наблюдателей и вызывает у каждого из них метод HandleEvent() (возможно, передавая в качестве аргумента состояние наблюдаемого объекта).

Рис. 13. Дизайн шаблона Наблюдатель.

Далее приводится пример кода, демонстрирующего реализацию шаблона Наблюдатель. Заметим, что в примере намеренно не используются события C#.

using System;

using System.Collections.Generic;

// аналог Subject

public class Stock

{

private int _price;

private IList<IInvestor> _investors = new List<IInvestor>();

public int Price

{

get { return _price; }

set

{

if (_price!= value)

{

_price = value;

Notify();

}

}

}

public Stock(int price)

{

_price = price;

}

public void Attach(IInvestor investor)

{

_investors.Add(investor);

}

public void Detach(IInvestor investor)

{

_investors.Remove(investor);

}

public void Notify()

{

foreach (var investor in _investors)

{

investor.Update(this);

}

}

}

// интерфейс, аналогичный IObserver

public interface IInvestor

{

void Update(Stock stock);

}

// конкретный наблюдатель

public class Investor: IInvestor

{

public string Name { get; private set; }

public Investor(string name)

{

Name = name;

}

public void Update(Stock stock)

{

Console.WriteLine("Notified {0} of change to {1}",

Name, stock.Price);

}

}

public class ObserverExample

{

public static void Main()

{

var ibm = new Stock(120);

ibm.Attach(new Investor("Soros"));

ibm.Attach(new Investor("Berkshire"));

ibm.Price = 120;

ibm.Price = 121;

ibm.Price = 125;

}

}

Для унификации работы с шаблоном Наблюдатель платформа.NET предлагает два интерфейса. Интерфейс System.IObserver<T> реализуется наблюдателем. Этот интерфейс содержит методы, уведомляющие о новом событии, об ошибке в наблюдаемом объекте и о прекращении генерации событий.

public interface IObserver<in T>

{

void OnCompleted();

void OnError(Exception error);

void OnNext(T value);

}

Интерфейс System.IObservable<T> реализуется наблюдаемым объектом. В интерфейсе описан единственный метод Subscribe() для добавления наблюдателя. Чтобы удалить наблюдателя, нужно вызвать метод Dispose() у объекта, возвращённого методом Subscribe().

public interface IObservable<out T>

{

IDisposable Subscribe(IObserver<T> observer);

}

Изменим пример, представленный выше, используя типовые интерфейсы.

using System;

using System.Collections.Generic;

public class Stock: IObservable<int>

{

private int _price;

private IList<IInvestor> _investors = new List<IInvestor>();

public int Price

{

get { return _price; }

set

{

if (_price!= value)

{

_price = value;

Notify();

}

}

}

public Stock(int price)

{

_price = price;

}

public IDisposable Subscribe(IObserver<int> investor)

{

_investors.Add(investor);

return new Unsubscriber(_investors, investor);

}

public void Notify()

{

foreach (var investor in _investors)

investor.OnNext(_price);

}

private class Unsubscriber: IDisposable

{

private readonly IObserver<int> _investor;

private readonly IList<IObserver<int>> _investors;

public Unsubscriber(IList<IObserver<int>> investors,

IObserver<int> investor)

{

_investors = investors;

_investor = investor;

}

public void Dispose()

{

_investors.Remove(_investor);

}

}

}

public class Investor: IObserver<int>

{

public string Name { get; private set; }

public Investor(string name)

{

Name = name;

}

public void OnNext(int value)

{

Console.WriteLine("Notified of change to {0}", value);

}

public void OnError(Exception error) { }

public void OnCompleted() { }

}

public class ObserverExample

{

public static void Main()

{

var ibm = new Stock(120);

ibm.Subscribe(new Investor("Soros"));

ibm.Subscribe(new Investor("Berkshire"));

ibm.Price = 120;

ibm.Price = 121;

ibm.Price = 125;

}

}


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: