我写了一个通知管理器,对象可以订阅特定的消息类型,为管理员提供回调。然后通过通知管理器的发布通知对象。它看起来像这样:
[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.");
}
我的对象将NotificationManager实例作为其构造函数中的依赖项。我的DI系统负责确保它提供NotificationManager的单例实例。
我一直在玩的另一种方法就是使用事件。这工作正常,除了我必须确保并提供我的所有对象,对他们需要订阅的对象的引用。在下面的示例中,我必须为下面的类(ClientHandler
类)提供对IPlayer
实例的引用。我还有一个Server
类,其中包含与ClientHandler
关联的IPlayer
和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}");
}
通知管理器的好处是我可以通过让所有需要对事件做出反应的对象来模式化通信方式,使用NotificationManager来订阅这些事件。这有什么缺点吗?它也感觉更安全,因为我可以取消订阅NotificationManager单例,而不是试图确保在处理对象之前取消注册我的事件处理程序。我可以快速进行空检查,如果IPlayer
为空,只需取消订阅我正在持有的订阅。
NotificationManager还支持谓词,因此我只能处理适用于对象实际需要查看的内容的消息。一个例子是仅处理从与接收消息的角色相同的房间中的玩家发送的聊天消息
ISubscription subscription = notificationCenter.Subscribe<ShoutMessage>(
(msg, sub) => { /* ... Do stuff .. */ sub.Unsubscribe(); },
msg => ((DefaultPlayer)msg.Sender).CurrentRoom == this.CurrentRoom);
你们看到使用pub / sub构建整个应用程序而不仅仅是使用事件的缺点吗?它们都有使用强引用订阅对象的相同问题。我认为调试pub / sub通常很困难,因为callstack很臃肿。
我在两者之间看到的唯一的另一个区别是,通过让所有内容仅通过NotificationManager
订阅而不是单个对象来进行更松散的耦合。
你们对pub / sub vs eventing有什么样的经历?我不是要求哪个更好。我想在这里体验你们两个人以及结果如何。