Looking for tips about implementation of Mediator Design Pattern in C#

Hello guys, I want to use the Mediator Design Pattern in my projects to create more robust and responsive User Interfaces. I am used to use this pattern wtih Javascript since its allow you to do Black Magic ( to call functions with variable names and variable number and type of parameters) but I never did it in C#, so I am looking for tips about its implementation.

Basically I want a mediator object to receive messages about updates in any area of the system. Then the mediator would “spread” these news to other objects interested on it.

Lets say that I have a list of players, when I click on ‘delete’ Buttom from a user from it I could just publish this event:

 mediator.publish( "delete", [userIndex, "Dead"]);

And the interested objects that were subscribed to this mediator would be responsible to response to this update. A text label could just update the number of users, the dataManager could delete it from the data manager and it goes on.

Looking for available solutions I found some links like this Javascript one or the SendMessage function from Unity3D but any of these seem to be the solution for me. Then I tried to adapt the idea that I did on my Javascript Project.

I didn’t test the following code, it was just a sketch code:

/** Class responsible to mediate messages in the system **/
public class Mediator
{
   // List of objects that will be notified about any news in the system
   public List<MediatorSubscriber> subscribers;
   
   public void subscribe( MediatorSubscriber ms )
   {
     subscribers.Add( ms );
   }

   public virtual void publish( string key, List<Object> pars )
   {
     foreach( MediatorSubscriber ms in subscribers )
     {
       ms.receiveNews( key, pars );
     }
   }
   
   public Mediator()
   {
     //does nothing
   }
}

/** Class that represent the objects interested in system updates **/
public class MediatorSubscriber
{
   public virtual void receiveNews( string key, List<Object> pars )
   {
     Debug.LogError("News system not configured");
   }   
}

/** Example of a kind of a generic subscriber interested in data updates **/
public class DataSubscriber :: MediatorSubscriber
{
   public virtual void deleteUser( int index, string cause )
   {
     //does nothing
   }
   
   public virtual void usersLoaded( List<Users> u )
   {
     //does nothing
   }

   // Convert abstract news to commands
   public override void receiveNews( string key, List<Object> pars )
   {
     if( key == "delete" )
     {
       Integer inx = pars[0] as int;
       string cause = pars[1] as string;
       deleteUser( inx, cause );
     }
     else if( key == "usersLoaded" )
     {
       List<User> users = pars[0] as List<Users>;
       usersLoaded( users );
     }
     else
     {
       Debug.LogError("Invalid key");
     }
   }   
}

/** Example of a subscriber specialization to be used in a list or table game object **/
public class UIListManipulator : DataSubscriber, MonoBehaviour
{
   // Filled by Inspector drag and drop
   public GameObject listPanel;
   public Mediator mediator;
   
   void Start()
   {
     mediator.subscribe( this );
   }
   
   public override void usersLoaded( List<Users> u )
   {
     // fill the UI list with a loop
   }
   
   public override void deleteUser( int index, string cause )
   {
     // delete the line with the deleted user
   }
}

This way I would have a well-documented way to describe event messages and decouple the UI part from the rest of system, what do you guys think about it, do you have suggestions? Thanks for the attention.

It looks like Mediator pattern to me.

Though I have a couple things I’d suggest.

  1. Instead of having the class ‘MediatorSubscriber’ to pass in. Just use a delegate type, this way anyone who wants to subscribe just passes in the method that want to be used as the callback.

  2. Personally I don’t like strings for stuff like id’n what command is sent, it makes managing code horrible. And as you add more and more of them, you get this longer and longer switch/else statement parsing them all out. Maybe consider having a class that every kind of published key inherits from. Lets call it ‘Command’. When you define a new command, you inherit from ‘Command’, declare the parameters for that command as fields, and you pass an instance of that to publish.

public class Command
{

}

public class DeleteCommand : Command
{
    public int Inx;
    public string Cause;
}

public class UserLoadedCommand : Command
{
    List<User> Users;
}

public delegate void MediatorCallback(Command c);

public class Mediator
{
    //note I have subscribe, though you could just go with the C# events model as well
    private MediatorCallback _subscribers;

    public void Subscribe(MediatorCallback callback)
    {
        _subscribers += callback;
    }

    public void Publish(Command c)
    {
        if(_subscribers != null) _subscribers(c);
    }
}

I actually have something like this… a bit. Though with mine you can select who you subscribe to listen to, OR you can subscribe to listen for any command globally. It’s used more for sending messages between GameObjects in game… but it’s a similarish. Also the mediator is a static entry point. So only one global mediator ever. Though the actual mediator is instanced per object, and can be treated individually that way.

Furthermore I make it where when you subscribe, you can subscribe for a specific command. In my case I call them ‘Notification’.

Notification - acts as the class all commands should inherit from, as well as the static entry for subscribing:
https://code.google.com/p/spacepuppy-unity-framework/source/browse/trunk/SpacepuppyBase/Notification.cs

NotificationDispatcher - the actual mediator instances:
https://code.google.com/p/spacepuppy-unity-framework/source/browse/trunk/SpacepuppyBase/INotificationDispatcher.cs

SPNotifyingComponent - acts as a wrapper so you can easily create a MonoBehaviour that can act as a mediator:
https://code.google.com/p/spacepuppy-unity-framework/source/browse/trunk/SpacepuppyBase/SPNotifyingComponent.cs

Then with it you declare a notification:

public class DeleteCommand : Notification
{
    public enum DeleteCause
    {
        CauseISaidSo = 0,
        CauseTheySuck = 1,
        NoReasonAtAll = 2
    }

    public int Inx;
    public DeleteCause Cause;
}

//lets say this is a user mediator... lets make this a simple singleton as well
public class UserMediator : SPNotifyingComponent
{
    //implied singleton enforcement
    public static UserMediator Instance;
 
 
    private void Update()
    {
        //something occurs that we know user was deleted
        this.PostNotification<DeleteCommand>(new DeleteCommand()
                                                { Inx = 0,
                                                 Cause = DeleteCommands.DeleteCause.CauseISaidSo },
                                            false);
        //alternative through static interface
        Notification.PostNotification<DeleteCommand>(this,
                                                    new DeleteCommand()
                                                       { Inx = 0,
                                                         Cause = DeleteCommands.DeleteCause.CauseISaidSo },
                                                    false);
    }
 
}

//registering for it:
UserModerator.Instance.RegisterObserver<DeleteCommand>(this.OnDeleteCommand);
//alternative through static interface
Notificaiton.RegisterObserver<DeleteCommand>(UserModerator.Instance, this.OnDeleteCommand);

private void OnDeleteCommand(DeleteCommand c)
{
    //react to it yo
}

Note, you might be wondering why in the hell the static interface for registering and posting exists. This is to ease integration with ‘GameObject’. There’s a special component that can be attached to a GameObject to turn it into a NotificationDispatcher… but GameObject isn’t actually typed that, and we don’t want to put the component on there unless it actually truly needs it. So the static interface handles doing that.

Personally I end up using the static interface for everything just so the code looks consistent, may I register with an actual NotificationDispatcher, or a GameObject that is an implied NotificationDispatcher.

lordofduct many thanks for the detailed answer. To be sincere that library that you linked here looks pretty interesting but also the Mediator section looks too big for me, I am afraid to get a bug and have to lose some days to find it there so I think that my simple solution would be fine for now.

About the ‘delegate’ solution that you give let me ask some questions:

  • How should the “deleteSubscriber” work in this approach?
  • Can you show some tiny example of a pratic implementation of “MediatorCallback(Command c)” with those 2 commands?

Yeah, the Notification one can get very complicated in its code. That’s mainly because I wanted it to be a component, and to implicitly support GameObjects as dispatchers. Furthermore I wanted to support inheritance with my Notifications… for instance if there is a ‘WeaponStrikeNotification’, and then there is a ‘SwordStrikeNotification’ which inherits from ‘WeaponStrikeNotification’. I wanted it so I could register for the ‘WeaponStrikeNotification’ and still receive them for both.

This led to some more complicated code.

Note though, it’s a library I’ve been writing for a long while, and I use in actual video games, like the one here http://jupiterlighthousestudio.com/invited-talk-globalgamejam-boca-raton/. I have put the Notification engine through the ringer to get the bugs out of it.

Anyways, I’ll show you what you asked for, but I’ma add in the ‘type’ constraint as well, so you don’t have to do the ‘switch/elseif’ commands in the callbacks. It won’t support inheritance though.

First… just for clarity sake, here’s how you’d remove a subscriber in that generic case I showed above:

public void DeleteSubscriber(MediatorCallback callback)
{
    _subscribers -= callback;
}

Here is a simple version with the type restriciton added.

public class Command
{

}

public delegate void MediatorCallback<T>(T c) where T : Command;

public class Mediator
{
    //make sure you're using the System.Collections.Generic namespace
    private Dictionary<System.Type, System.Delegate> _subscribers = new Dictionary<System.Type, System.Delegate>();

    public void Subscribe<T>(MediatorCallback<T> callback) where T : Command
    {
        if(callback == null) throw new System.ArgumentNullException("callback");
        var tp = typeof(T);
        if(_subscribers.ContainsKey(tp))
            _subscribers[tp] = System.Delegate.Combine(_subscribers[tp], callback);
        else
            _subscribers.Add(tp, callback);
    }

    public void DeleteSubscriber<T>(MediatorCallback<T> callback) where T : Command
    {
        if(callback == null) throw new System.ArgumentNullException("callback");
        var tp = typeof(T);
        if(_subscribers.ContainsKey(tp))
        {
            var d = _subscribers[tp];
            d = System.Delegate.Remove(d, callback);
            if(d == null) _subscribers.Remove(tp);
            else _subscribers[tp] = d;
        }
    }

    public void Publish<T>(T c) where T : Command
    {
        var tp = typeof(T);
        if(_subscribers.ContainsKey(tp))
        {
            _subscribers[tp].DynamicInvoke(c);
        }
    }
}

And example use:

public class DeleteCommand : Command
{
    public int Inx;
    public string Cause;
}

public class UserLoadedCommand : Command
{
    public List<User> Users;
}

public class UIListManipulator : MonoBehaviour
{
    public GameObject listPanel;
    public Mediator mediator;

    void OnEnable()
    {
        mediator.Subscribe<DeleteCommand>(this.OnDelete);
        mediator.Subscribe<UserLoadedCommand>(this.OnUserLoadedCommand);
    }

    void OnDisable()
    {
        mediator.DeleteSubscriber<DeleteCommand>(this.OnDelete);
        mediator.DeleteSubscriber<UserLoadedCommand>(this.OnUserLoadedCommand);
    }

    private void OnDelete(DeleteCommand c)
    {
        if(c.Inx == 0)
        {
            switch(c.Cause)
            {
                case "blargh":

                    break;
                case "blurgh":

                    break;
            }
        }
    }

    private void OnUserLoadedCommand(UserLoadedCommand c)
    {
        foreach(var u in c.Users)
        {
            //display u
        }
    }
}


public class SomeUserCommandDistributor
{

    public Mediator mediator;

    public void Foo()
    {
        //some place we're dispatching this command
        mediator.Publish<DeleteCommand>(new DeleteCommand() { Inx = 0, Cause = "blargh" });
    }

}