Wednesday, August 7, 2013

Another Observer Pattern implementation (with expressions and delegates)

This is an example of the Observer Pattern implementation in C# where the registration and announcing about events is handled by a separate class Observation. It has two public methods: one for registering for an action (used by subscribers) and one for informing about events (used by publishers). Expression is used to capture the method name the observer wants to register for and Action delegate is used to capture the method name that the subject wants to raise.

public interface ISubject<T>
{
  void RegisterFor(Expression<Action<T>> action, Action callback);
}

public interface IObservers
{
  void InformOf(Action action);
}
  
public class Observation<T> : IObservers, ISubject<T>
{
  private readonly Dictionary<string, Action> callbacks = new Dictionary<string, Action>();

  public void RegisterFor(Expression<Action<T>> action, Action callback)
  {
    var key = KeyFrom(action);
    if (callbacks.ContainsKey(key))
      callbacks[key] += callback;
    else
      callbacks[key] = callback;
  }

  public void InformOf(Action action)
  {
    var key = KeyFrom(action);
    if (callbacks.ContainsKey(key))
      callbacks[key]();
  }

  private string KeyFrom(Action action)
  {
    return action.Method.Name;
  }

  private string KeyFrom(Expression<Action<T>> action)
  {
    var methodExpressionSignature = (action.Body as MethodCallExpression).ToString();
    return ExtractMethodName(methodExpressionSignature);
  }

  private string ExtractMethodName(string signature)
  {
    return signature.Split(new[] { '.', '(' })[1];
  }
}

An interface is introduced to specify the actions being observed:

public interface IApplicationState
{
  void Starting();
  void Stopping();
}

public class Application : IApplicationState
{
  IObservers _observers;
  public Application(IObservers observers)
  {
    _observers = observers;
  }

  public void Starting()
  {
    /*...*/
    _observers.InformOf(Starting);
  }

  public void Stopping()
  {
    /*...*/
    _observers.InformOf(Stopping);
  }
}

An example of use:

static void Main(string[] args)
{
  var observation = new Observation<IApplicationState>();
  var app = new Application(observation);

  observation.RegisterFor(action: change => change.Starting(),
                          callback: () => Console.WriteLine("application is starting."));

  app.Starting();
  app.Stopping();

  Console.ReadLine();
}
//OUTPUT:
//application is starting.

No comments:

Post a Comment