In the previous code sample, we named the classes after the Mediator pattern actors, as shown in Figure 14.7. While this example is very similar, it uses domain-specific names instead and implements a few more methods to manage the system showing a more tangible implementation. Let’s start with the abstractions:
namespace Mediator;
public interface IChatRoom
{
void Join(IParticipant participant);
void Send(ChatMessage message);
}
The IChatRoom interface is the mediator, and it defines two methods instead of one:
- Join, which allows IParticipant to join IChatRoom.
- Send, which sends a message to the others.
public interface IParticipant
{
string Name { get; }
void Send(string message);
void ReceiveMessage(ChatMessage message);
void ChatRoomJoined(IChatRoom chatRoom);
}
The IParticipant interface is the colleague and also has a few more methods:
- Send, to send messages.
- ReceiveMessage, to receive messages from the other IParticipant objects.
- ChatRoomJoined, to confirm that the IParticipant object has successfully joined a chatroom.
public record class ChatMessage(IParticipant Sender, string Content);
The ChatMessage class is the same as the previous Message class, but it references IParticipant instead of IColleague.Let’s now look at the IParticipant implementation:
public class User : IParticipant
{
private IChatRoom?
_chatRoom;
private readonly IMessageWriter<ChatMessage> _messageWriter;
public User(IMessageWriter<ChatMessage> messageWriter, string name)
{
_messageWriter = messageWriter ??
throw new ArgumentNullException(nameof(messageWriter));
Name = name ??
throw new ArgumentNullException(nameof(name));
}
public string Name { get; }
public void ChatRoomJoined(IChatRoom chatRoom)
{
_chatRoom = chatRoom;
}
public void ReceiveMessage(ChatMessage message)
{
_messageWriter.Write(message);
}
public void Send(string message)
{
if (_chatRoom == null)
{
throw new ChatRoomNotJoinedException();
}
_chatRoom.Send(new ChatMessage(this, message));
}
}
public class ChatRoomNotJoinedException : Exception
{
public ChatRoomNotJoinedException()
: base(“You must join a chat room before sending a message.”)
{ }
}
The User class represents our default IParticipant. A User instance can chat in only one IChatRoom. THe program can set the chat room by calling the ChatRoomJoined method. When it receives a message, it delegates it to its IMessageWriter<ChatMessage>. Finally, a User instance can send a message through the mediator (IChatRoom). The Send method throws a ChatRoomNotJoinedException to enforce that the User instance must join a chat room before sending messages (code-wise: the _chatRoom field must not be null).We could create a Moderator, Administrator, SystemAlerts, or any other IParticipant implementation as we see fit, but not in this sample. I am leaving that to you to experiment with the Mediator pattern.Now let’s look at the ChatRoom class (the mediator):
public class ChatRoom : IChatRoom
{
private readonly List<IParticipant> _participants = new();
public void Join(IParticipant participant)
{
_participants.Add(participant);
participant.ChatRoomJoined(this);
Send(new ChatMessage(participant, “Has joined the channel”));
}
public void Send(ChatMessage message)
{
_participants.ForEach(participant
=> participant.ReceiveMessage(message));
}
}
Leave a Reply