I like the Visitor approach above. However just for the fun of it I show a bit on how to reduce redundancy in code using T4 in VS2008 and VS2010.
The redundancy comes from that for each message you need a Visit method. Also each method needs a simple but redundant implementation of Accept. One way to get closer to "Do not repeat yourself" is generating the code using T4.
In order to test the following sample add a class in VS but change the extension from .cs to .tt. You will now get two files a .tt file and a .cs file connected to the .tt file.
The .tt file is a template that generates .cs file. At the time they are identical.
Use this as the content for the .tt file:
<#@ template language="C#" #>
<#
// On VS2008 change C# above to C#v3.5
// -----------------------------------------------------
// Here we declare our different message types
var messageTypes = new []
{
"Simple",
"Complex",
"Other",
};
// -----------------------------------------------------
#>
namespace MessageProcessor
{
partial interface IMessageVisitor
{
<#
// Let's generate all message visitor methods
foreach (var messageType in messageTypes)
{
#>
void Visit (<#=messageType#>Message message);
<#
}
#>
}
abstract partial class Message
{
public abstract void Accept (IMessageVisitor visitor);
}
<#
// Let's generate all message types
foreach (var messageType in messageTypes)
{
#>
sealed partial class <#=messageType#>Message : Message
{
public override void Accept (IMessageVisitor visitor)
{
visitor.Visit (this);
}
}
<#
}
#>
}
This should generate a CS file that looks like this:
namespace MessageProcessor
{
partial interface IMessageVisitor
{
void Visit (SimpleMessage message);
void Visit (ComplexMessage message);
void Visit (OtherMessage message);
}
abstract partial class Message
{
public abstract void Accept (IMessageVisitor visitor);
}
sealed partial class SimpleMessage : Message
{
public override void Accept (IMessageVisitor visitor)
{
visitor.Visit (this);
}
}
sealed partial class ComplexMessage : Message
{
public override void Accept (IMessageVisitor visitor)
{
visitor.Visit (this);
}
}
sealed partial class OtherMessage : Message
{
public override void Accept (IMessageVisitor visitor)
{
visitor.Visit (this);
}
}
}
Why is this less redudant? Because now whenever I like to add a new meessage I just add it to the template:
var messageTypes = new []
{
"Simple",
"Complex",
"Other",
"YetAnotherOne",
};
It's important to note that all messages are generated as partial because we need different payloads for a message. This is specified in another file and it could look like this:
partial class SimpleMessage
{
public string Name;
}
partial class ComplexMessage
{
public XmlDocument Xml;
}
For those that likes the sound of T4 check this blog: http://www.olegsych.com/2008/09/t4-tutorial-creatating-your-first-code-generator/