Страницы

Поиск по вопросам

пятница, 9 ноября 2018 г.

Как организовать взаимодействие объекта с его наблюдателем

Допустим у меня есть класс A. В классе А есть набор эвентов, которые отражают какие либо изменения внутри - изменения коллекций, изменение основных свойств/состояния. И у объекта есть свойство - объект класса B, который должен следить за этими состояниями и производить какую либо работу проанализировав набор всех изменений.
Логично чтобы при создании объекта класса B, передавать ему в конструктор ссылку на объект класса A и привязываться к событиям.
Интересует есть какие либо другие способы, реализации или паттерны.


Ответ

Интересует есть какие либо другие способы, реализации или паттерны
В С# паттерн "наблюдатель" может быть реализован многими способами, например:
На основе делегата На основе события При помощи строго типизированного интерфейса При помощи специальных интерфейсов IObserver/IObservable
На основе делегата
Решение на основе делегата предствляет собой классических колбек. Преимуществом данного метода является отсутствие явного, типизированного обработчика события (взаимоотношение между поставщиком и потребителем события никак не регламентируются) и простота метода – при возникновении события, метод класса Consumer вызывает аргумент-делегат:
class Consumer { public void Event(Action callback) { ...
callback(); } }
При возникновении события будет вызван делегат act и весь его invocation list, таким образом поддерживается множественность обработки события.
На основе события
Метод похож на предыдущий, но с той разницей, что позволяет организовать подписку на события любому количеству обработчиков (без моделирования отношения 1:1 как в предыдущем методе) либо не иметь обработчиков вовсе.
class Consumer { public event EventHandler Event;
void RaiseEvent() { // Не самый потокобезопасный код if (Event != null) { Event.Invoke(this, new EventArgs()); } } }
public class Program { public static void Main() { Consumer consumer = new Consumer();
consumer.Event += (object sender, EventArgs args) => { /* */ }; consumer.Event += (object sender, EventArgs args) => { /* */ }; } }
Метод похож на предыдущий но не дает гарантий на присутствие хотя бы одного обработчика.
При помощи строго типизированного интерфейса
В этом методе потребитель обрабатывает события поставщика при помощи определенного интерфейса обработчика, строго формализуя отношения между поставщиком и потребителем.
interface IEventHandler { void FirstEventHandler(); void SecondEventHandler(); }
class Supplier : IEventHandler { public void FirstEventHandler() { /* Обработчик события */ }
public void SecondEventHandler() { /* Обработчик другого события */ } }
class Consumer { IEventHandler _supplier;
public void Subscribe(IEventHandler supplier) { _supplier = supplier; }
void RaiseFirstEvent() { _supplier.FirstEventHandler(); }
void RaiseSecondEvent() { _supplier.SecondEventHandler(); } }
public class Program { public static void Main() { Consumer consumer = new Consumer(); Supplier supplier = new Supplier();
consumer.Subscribe(supplier); } }
Это почти классическая реализация паттерна подписчик в C# (и паттерна "делегат" в Objective-C с той лишь разницей, что все обработчики должны быть строго определены).
Реализация этого метода требует пристального наблюдения на предмет нарушения SRP, так и согласованности событий поставщика.
При помощи специальных интерфейсов IObserver/IObservable
Начиная с .NET 4 доступны специальные интерфейсы IObserver/IObservable для реализации паттерна наблюдатель для последовательностей событий
class Data {
}
class Consumer : IObserver { public void OnCompleted() {
}
public void OnError(Exception e) {
}
public void OnNext(Data data) { // Обработка новых данных } }
class Supplier : IObservable { List> _subscribers = new List>();
class Unsubscriber : IDisposable { private List>_observers; private IObserver _observer;
public Unsubscriber(List> observers, IObserver observer) { _observers = observers; _observer = observer; }
public void Dispose() { if (_observer != null && _observers.Contains(_observer)) _observers.Remove(_observer); } }
public IDisposable Subscribe(IObserver observer) { _subscribers.Add(observer);
return new Unsubscriber(_subscribers, observer); }
void RaiseEvent() { foreach (var subscriber in _subscribers) { subscriber.OnNext(new Data()); } } }
public class Program { public static void Main() { Consumer consumer = new Consumer(); Supplier supplier = new Supplier();
supplier.Subscribe(consumer); } }

Комментариев нет:

Отправить комментарий