Question:
Let's say I have a class A. In class A there is a set of events that reflect any changes inside – changes in collections, changes in basic properties / state. And the object has a property – an object of class B, which must monitor these states and perform some work by analyzing the set of all changes.
It is logical that when creating an object of class B, pass it to the constructor a reference to an object of class A and bind to events.
I am interested in any other methods, implementations or patterns.
Answer:
I'm interested in any other ways, implementations or patterns
In C #, the observer pattern can be implemented in many ways, for example:
- Delegate based
- Event based
- Using a strongly typed interface
- Using special interfaces
IObserver/IObservable
Delegate based
The delegate solution is a classic callback. The advantage of this method is the absence of an explicit, typed event handler (the relationship between the provider and the consumer of the event is not regulated in any way) and the simplicity of the method – when an event occurs, the method of the Consumer
class calls the delegate argument:
class Consumer
{
public void Event(Action callback)
{
...
callback();
}
}
When an event occurs, the act
delegate and its entire invocation list will be called, thus supporting multiple event handling.
Event based
The method is similar to the previous one, but with the difference that it allows you to organize subscription to events with any number of handlers (without modeling a 1: 1 ratio as in the previous method) or not have handlers at all.
class Consumer
{
public event EventHandler<EventArgs> 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) => { /* */ };
}
}
The method is similar to the previous one, but does not guarantee the presence of at least one handler.
Using a strongly typed interface
In this method, the consumer handles the events of the provider using a specific handler interface, strictly formalizing the relationship between the provider and the consumer.
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);
}
}
This is almost a classic implementation of the subscriber pattern in C # (and the delegate pattern in Objective-C, with the only difference that all handlers must be strictly defined).
Implementing this method requires close monitoring for SRP violations and vendor event consistency.
Using special interfaces IObserver / IObservable
Since .NET 4, special IObserver/IObservable
interfaces are available to implement the observer pattern for event sequences :
class Data {
}
class Consumer : IObserver<Data>
{
public void OnCompleted()
{
}
public void OnError(Exception e)
{
}
public void OnNext(Data data)
{
// Обработка новых данных
}
}
class Supplier : IObservable<Data>
{
List<IObserver<Data>> _subscribers = new List<IObserver<Data>>();
class Unsubscriber : IDisposable
{
private List<IObserver<Data>>_observers;
private IObserver<Data> _observer;
public Unsubscriber(List<IObserver<Data>> observers, IObserver<Data> observer)
{
_observers = observers;
_observer = observer;
}
public void Dispose()
{
if (_observer != null && _observers.Contains(_observer))
_observers.Remove(_observer);
}
}
public IDisposable Subscribe(IObserver<Data> 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);
}
}