I am building out a MUD game engine and want my objects to communicate with each other. There is a wide range of message types that will be sent around, all of which implement IMessage. I adopted the Mediator pattern to facilitate passing the messages around to objects that have subscribed for publications. Is the pattern being used properly here?INotificationCenter and implementationThe interface represents what objects will interact with when they want to subscribe to publications for a specific IMessage type. The ChatCenter is implemented as a singleton and will ultimately end up with the Dictionary being changed to a ConcurrentDictionary for thread-safety.public interface INotificationCenter{    ISubscriptionHandler Subscribe<T>() where T : class, IMessage;    void Publish<T>(T message) where T : class, IMessage;}/// <summary>/// The mediator for all messaging/// </summary>public class ChatCenter : INotificationCenter{    /// <summary>    /// Collection of subscribed listeners    /// </summary>    private Dictionary<Type, List<ISubscriptionHandler>> listeners =        new Dictionary<Type, List<ISubscriptionHandler>>();    private static ChatCenter _centerSingleton = new ChatCenter();    private ChatCenter()    {    }    /// <summary>    /// Subscribe publications for the message type specified.    /// </summary>    /// <typeparam name=T></typeparam>    /// <returns></returns>    public ISubscriptionHandler Subscribe<T>() where T : class, IMessage    {        Type messageType = typeof(T);        if (!listeners.ContainsKey(messageType))        {            listeners.Add(messageType, new List<ISubscriptionHandler>());        }        // TODO: Move instancing of the handler in to a factory that does a lookup on <T> and returns the right handler.        var handler = new ChatMessageHandler();        listeners[messageType].Add(handler);        return handler;    }    public static ChatCenter CurrentCenter    {        get        {            return _centerSingleton;        }    }    /// <summary>    /// Publishes the specified message to all subscribers    /// </summary>    /// <typeparam name=T></typeparam>    /// <param name=message>The message.</param>    public void Publish<T>(T message) where T : class, IMessage    {        if (!listeners.ContainsKey(typeof(T)))        {            return;        }        foreach (var handler in listeners[typeof(T)])        {            message.Dispatch(handler);        }    }    internal void Unsubscribe<T>(ISubscriptionHandler handler) where T : class, IMessage    {        if (!listeners.ContainsKey(typeof(T)))        {            return;        }        listeners.Remove(typeof(T));    }}ISubscriptionHandler and implementation.The subscription handlers are what are used to define what happens when a publish happens. At the moment, they just provide predicates and an Action to invoke. A non-generic type is used so that my ChatCenter does not have to be generic. There are different types of chat messages that can be used./// <summary>/// Provides a contract to Types wanting to subscribe to published messages /// with conditions and a callback./// </summary>public interface ISubscriptionHandler{    ISubscriptionHandler If(Func<IMessage, bool> condition);    ISubscriptionHandler Dispatch(Action<IMessage> message);    void Unsubscribe();}    /// <summary>/// Processes a subscription message./// </summary>/// <typeparam name=TMessageType>The type of the message type.</typeparam>public interface ISubscriptionProcessor<TMessageType> : ISubscriptionHandler{    void ProcessMessage(TMessageType message);}/// <summary>/// Handles chat message subscriptions/// </summary>public class ChatMessageHandler : ISubscriptionProcessor<ChatMessage>{    private List<Action<IMessage>> callbacks = new List<Action<IMessage>>();    private List<Func<IMessage, bool>> conditions = new List<Func<IMessage, bool>>();    /// <summary>    /// Registers a callback for when a chat message is published by the MessageCenter    /// </summary>    /// <param name=message>The message.</param>    /// <returns></returns>    public ISubscriptionHandler Dispatch(Action<IMessage> message)    {        this.callbacks.Add(message);        return this;    }    /// <summary>    /// Provides conditional values that will be evaluated upon a publish from the MessageCenter.    /// If any of the results return false, the callbacks will not be dispatched.    /// </summary>    /// <param name=condition>The condition.</param>    /// <returns></returns>    public ISubscriptionHandler If(Func<IMessage, bool> condition)    {        this.conditions.Add(condition);        return this;    }    public void Unsubscribe()    {        this.callbacks.Clear();        this.conditions.Clear();        ChatCenter.CurrentCenter.Unsubscribe<ChatMessage>(this);    }    /// <summary>    /// Processes the message by verifying the callbacks can be invoked, then invoking them.    /// </summary>    /// <param name=message>The message.</param>    public void ProcessMessage(ChatMessage message)    {        // If any of the conditions fail, don't process.        if (conditions.Any(condition => !condition(message)))        {            return;        }        // Invoke each callback.        foreach (var callback in this.callbacks)        {            callback(message);        }    }}IMessage and implementationLastly is the IMessage interface and a sample implementation for a chat message./// <summary>/// A contract for objects wanting to dispatch message notifications./// </summary>public interface IMessage{    void Dispatch(ISubscriptionHandler handler);}/// <summary>/// Provides methods for dispatching notifications to subscription handlers/// </summary>/// <typeparam name=TMessageType>The type of the message type.</typeparam>public class MessageBase<TMessageType> : IMessage where TMessageType : class, IMessage{    /// <summary>    /// Dispatches this message instance to the given handler for processing.    /// </summary>    /// <param name=handler>The handler.</param>    public void Dispatch(ISubscriptionHandler handler)    {        // We must convert ourself to our generic type.        var msg = this as TMessageType;        if (msg == null)        {            return;        }        var target = handler as ISubscriptionProcessor<TMessageType>;        if (target == null)        {            return;        }        // Dispatch ourself strongly typed to a protected version        // of the Dispatch method.        this.Dispatch(target, msg);    }    /// <summary>    /// Dispatches the given message to the given handler.    /// Children classes can override this method to perform custom dispatching    /// if needed.    /// </summary>    /// <param name=target>The handler.</param>    /// <param name=message>The message.</param>    protected virtual void Dispatch(ISubscriptionProcessor<TMessageType> target, TMessageType message)    {        // Let the handler process this message.        target.ProcessMessage(message);    }}/// <summary>/// A chat message./// </summary>public class ChatMessage : MessageBase<ChatMessage>{    public ChatMessage(string message)    {        this.Message = message;    }    public string Message { get; private set; }}Unit Test Demonstrating usage.[TestClass]public class SubscriptionResultTests{    [TestMethod]    public void Conditions_for_publish_are_met_and_dispatched()    {        // Arrange        var chatHandler = new ChatMessageHandler();        int x = 1;        int y = 2;        int z = 3;        var center = ChatCenter.CurrentCenter;        // Subscribe        center.Subscribe<ChatMessage>()            .If(msg => x == 1)            .If(msg => y == 2)            .If(msg => z == 3)            .Dispatch(msg => x = 3)            .Dispatch(msg =>            {                y = 10;                                                                                                                                                                                                                                                 z = 15;            });        // Act        center.Publish(new ChatMessage(string.Empty));        // Assert        Assert.AreEqual(3, x);        Assert.AreEqual(10, y);        Assert.AreEqual(15, z);    }    [TestMethod]    public void Object_can_unsubscribe()    {        // Arrange        var chatHandler = new ChatMessageHandler();        int y = 2;        var callback = new Action<IMessage>(msg => y = 10);        // Subscribe        ISubscriptionHandler handler = ChatCenter.CurrentCenter.Subscribe<ChatMessage>()            .Dispatch(callback);        handler.Unsubscribe();        // Act        ChatCenter.CurrentCenter.Publish(new ChatMessage(string.Empty));        // Assert        Assert.AreEqual(2, y);    }}One of the downsides of how I've built it is that my callback's are not strongly typed to the subscribed IMessage implementation, so I can't access any custom properties within each IMessage implementation during a dispatch.
Mediator pattern implementation for game messaging
c#;design patterns;mediator
ISubscriptionHandler Subscribe<T>()I think this is a confusing interface. I would expect something along the lines of:ISubscription Subscribe<T>(ISubscriptionHandler<T> handler)where ISubscriptionHandler<T> would contain any predicates and code that should be dispatched when a message arrives and ISubscription can be used to unsubscribe.Why is ChatCenter a singleton? This is especially weird since you're using unit tests, which should be independent./// <typeparam name=T></typeparam>/// <returns></returns>If you don't want to add documentation comments for everything, that's fine. But there is no reason to have empty comments like these cluttering up your code.You already realized that you need to separate the non-generic ISubscriptionHandler and the generic ISubscriptionProcessor<TMessage>. But since you already have that, you should use the generic version as much as possible and use the non-generic version only when necessary (pretty much just around the Dictionary).This would mean you could get rid of the IMessage.Dispatch() step and for example the core of ChatCenter.Publish() could look like this (explicitly typing the iteration variable of a foreach works as a cast):foreach (ISubscriptionProcessor<T> handler in listeners[typeof(T)]){    handler.ProcessMessage(message);}public interface ISubscriptionHandler{    ISubscriptionHandler If(Func<IMessage, bool> condition);    ISubscriptionHandler Dispatch(Action<IMessage> message);    void Unsubscribe();}Even if you made this type generic as I suggested above, this interface still feels clunky to me. Why do you even need multiple conditions and multiple handler delegates? And if you think that having single condition is not enough, why would multiple conditions joined by AND be enough?Just allow only a single condition and if the user wants something more complicated, they can create that complicated condition themselves.Action<IMessage> messageThis delegate is not a message, so it shouldn't be called that. Maybe something like messageProcessor?public interface ISubscriptionProcessor<TMessageType>Generic parameter can't be anything else than a type, so you need to spell it out, just TMessage is enough. (Also The type of the message type. is confusing.)public class ChatMessageHandler : ISubscriptionProcessor<ChatMessage>This whole type doesn't contain anything that's specific to ChatMessage. Instead, it should be a generic type like:public class MessageHandler<TMessage> : ISubscriptionProcessor<TMessage>var msg = this as TMessageType;if (msg == null){    return;}This situation would mean that the current type of message is broken (it has incorrect type parameter). I think the right behavior in that case is to complain loudly by throwing an exception, not silently doing nothing.This way, the programmer who wrote the faulty message type will realize their mistake very soon, they won't have to spend time figuring out why their message doesn't seem to do anything.Very similar argument applies to the following handler check.