That class could hardly be simpler: it takes a name and an IMessageWriter<TMessage> implementation when created, then it stores a reference for future use.The IMessageWriter<TMessage> interface serves as a presenter and controls how the messages are displayed. The IMessageWriter<TMessage> interface is unrelated to the Mediator pattern. Nevertheless, it is a way to manage how a ConcreteColleague object outputs the messages without coupling it with a specific target. Here is the code:
namespace Mediator;
public interface IMessageWriter<Tmessage>
{
void Write(Tmessage message);
}
The consumer of the system is an integration test defined in the MediatorTest class. The test uses the chat system and asserts the output using a custom implementation of the IMessageWriter interface. Let’s start by analyzing the test:
namespace Mediator;
public class MediatorTest
{
[Fact]
public void Send_a_message_to_all_colleagues()
{
// Arrange
var (millerWriter, miller) = CreateConcreteColleague(“Miller”);
var (orazioWriter, orazio) = CreateConcreteColleague(“Orazio”);
var (fletcherWriter, fletcher) = CreateConcreteColleague(“Fletcher”);
The test starts by defining three colleagues with their own TestMessageWriter implementation (names were randomly generated).
var mediator = new ConcreteMediator(miller, orazio, fletcher);
var expectedOutput = @”[Miller]: Hey everyone!
[Orazio]: What’s up Miller?
[Fletcher]: Hey Miller!
“;
In the second part of the preceding Arrange block, we create the subject under test (mediator) and register the three colleagues. At the end of that Arrange block, we also define the expected output of our test. It is important to note that we control the output from the TestMessageWriter implementation (defined at the end of the MediatorTest class). Next is the Act block:
// Act
mediator.Send(new Message(
Sender: miller,
Content: “Hey everyone!”
));
mediator.Send(new Message(
Sender: orazio,
Content: “What’s up Miller?”
));
mediator.Send(new Message(
Sender: fletcher,
Content: “Hey Miller!”
));
In the preceding Act block, we send three messages through the mediator instance. Next is the Assert block:
// Assert
Assert.Equal(expectedOutput, millerWriter.Output.ToString());
Assert.Equal(expectedOutput, orazioWriter.Output.ToString());
Assert.Equal(expectedOutput, fletcherWriter.Output.ToString());
}
In the Assert block, we ensure that all colleagues receive the messages.
private static (TestMessageWriter, ConcreteColleague) CreateConcreteColleague(string name)
{
var messageWriter = new TestMessageWriter();
var concreateColleague = new ConcreteColleague(name, messageWriter);
return (messageWriter, concreateColleague);
}
The CreateConcreteColleague method is a helper method that encapsulates the creation of the colleagues, enabling us to write the one-liner declaration used in the Arrange section of the test. Next, we look at the IMessageWriter implementation:
private class TestMessageWriter : IMessageWriter<Message>
{
public StringBuilder Output { get; } = new StringBuilder();
public void Write(Message message)
{
Output.AppendLine($”[{message.Sender.Name}]: {message.Content}”);
}
}
} // Closing the MediatorTest class
Finally, the TestMessageWriter class writes the messages into StringBuilder, making it easy to assert the output. If we were to build a GUI for that, we could write an implementation of IMessageWriter<Message> that writes to that GUI; in the case of a web UI, it could use SignalR or write to the response stream directly, for example.To summarize the sample:
- The consumer (the unit test) sends messages to colleagues through the mediator.
- The TestMessageWriter class writes those messages to a StringBuilder instance. Each colleague has its own instance of the TestMessageWriter class.
- The code asserts that all colleagues received the expected messages.
This example illustrates that the Mediator pattern allows us to break the direct coupling between colleagues. The messages reached colleagues without them knowing about each other.Colleagues should communicate through the mediator, so the Mediator pattern would not be complete without that. Let’s implement a more advanced chatroom to tackle this concept.
Leave a Reply