1

I have wrote a notification manager that objects can subscribe to for specific message types, providing the manager with a callback. The objects are then notified via a publication from the notification manager. It looks something like this:

[TestMethod]
[TestCategory("Runtime.Game - NotificationManager")]
public void Publish_invokes_callbacks()
{
    // Arrange
    bool callbackCalled = false;
    string messageContent = "Test";
    INotificationCenter notificationCenter = new NotificationManager();
    ISubscription subscription = notificationCenter.Subscribe<ShoutMessage>(
        (msg, sub) => callback = msg.Content == messageContent);

    // Act
    notificationCenter.Publish(new ShoutMessage(messageContent));
    subscription.Unsubscribe();

    // Assert
    Assert.IsTrue(callbackCalled, "The subscriber did not have its callback invoked.");
}

My objects would take a NotificationManager instance as a dependency in its constructor. My DI system is responsible for making sure that it provides a singleton instance of the NotificationManager.

The other approach I have been playing with is just using eventing. This works fine, except that I have to make sure and provide all of my objects, a reference to the objects they need to subscribe to. In the following example, I have to give the class below (ClientHandler class) a reference to the IPlayer instance. I also have a Server class that has event handlers for events on both the ClientHandler and the IPlayer instance associated with the ClientHandler.

/// <summary>
/// Starts listening for network communication sent from the client to the server
/// </summary>
public void StartListeningForData()
{
    // Start listening for messages from the client.
    this.CurrentSocket.BeginReceive(this.Buffer, 0, bufferSize, 0, new AsyncCallback(this.ReceiveData), null);

    // Subscribe to the result of processing a command from the client.
    this.Player.CommandManager.CommandCompleted += this.HandleCommandExecutionCompleted;
}

private void HandleCommandExecutionCompleted(object sender, CommandCompletionArgs e)
{
    if (!this.IsConnectionValid)
    {
        return;
    }

    // Temporarily just send back the same content we received from the client.
    this.SendMessage($"{e.CommandResult.Result}");
}

The nice part of the notification manager is that I can pattern out the way things communicate, by having all the objects that need to react to events, use the NotificationManager to subscribe for those events. Is there any downsides to this? It also feels safer, as I can unsubscribe from the NotificationManager singleton instead of trying to make sure I unregister my event handlers prior to an object being disposed. I can do a quick null check, and if the IPlayer is null, just unsubscribe from the subscription I'm holding.

The NotificationManager also supports predicates, so that I can only handle messages that are applicable to what the object actually needs to see. An example is only handling chat messages that are sent from a player in the same room as the character receiving the message

ISubscription subscription = notificationCenter.Subscribe<ShoutMessage>(
    (msg, sub) => { /* ... Do stuff .. */ sub.Unsubscribe(); },
    msg => ((DefaultPlayer)msg.Sender).CurrentRoom == this.CurrentRoom);

Do you guys see a downside to building out an entire application using pub/sub vs just using eventing? They both have the same issue of subscribing to an object using strong references. I think debugging pub/sub can often times be difficult because the callstack is bloated.

The only other difference I see between the two is being a little more loosely coupled by having everything subscribe through the NotificationManager only, and not the individual objects.

What kind of experience have you guys had with pub/sub vs eventing? I'm not asking for which is better. I'm wanting to here your experience with both of them and how it turned out for you.

Johnathon Sullinger
  • 7,097
  • 5
  • 37
  • 102
  • 1
    Here's a question that might help you. Will your NotificationManager be expected to send messages outside of the process which it's running under? E.g. will it be a hosted service somewhere? If so, using delegates as a callback will likely be a big problem due to the need for data to be serialized across the wire. A big reason why events are used, and especially events with typed eventarg classes, is because they can be constructed in a manner that allows instances of them to be serialized/deserailized. – Trevor Ash May 22 '15 at 03:34
  • If you mean would my server, have a client outside of the process subscribe to messages, that the server has to publish to - then no. The server (telnet server) will take incoming socket traffic (from a telnet client) and push data back; the actual pub/sub stuff however is all internal to how the server works and isn't exposed outside of the process. – Johnathon Sullinger May 22 '15 at 04:03

0 Answers0