Problem
I would like to create a generic template class for my document processor. Assuming the template has a source file path and a “content”, here’s what I came up with:
Template
public interface ITemplate<T>
{
/// <summary>
/// The filepath of the base document for the template.
/// </summary>
String BaseDocumentFilePath { get; }
/// <summary>
/// Retrieve the underlying template source.
/// </summary>
/// <returns>T</returns>
T GetContent();
}
public class WordTemplate : ITemplate<XmlDocument>
{
private string _baseDocumentFilePath;
private XmlDocument _content;
public WordTemplate(XmlDocument content, string baseDocumentFilepath)
{
_content = content;
_baseDocumentFilePath = baseDocumentFilepath;
}
public string BaseDocumentFilePath { get { return _baseDocumentFilePath;}}
public XmlDocument GetContent()
{
return _content;
}
}
Ex Doc Generator
public interface IDocumentGenerator
{
/// <summary>
/// Generates a document from an instance of <see cref="ITemplate"/>.
/// </summary>
/// <param name="template">The template for the document.</param>
/// <param name="outputDocumentFilepath">The full file path of the result output.</param>
/// <returns>Returns filepath for generated document.</returns>
void Generate<T>(ITemplate<T> template, string outputDocumentFilepath);
}
public class WordDocumentGenerator : IDocumentGenerator
{
public void Generate<T>(ITemplate<T> template, string outputDocumentFilepath)
{
XmlDocument xmlDocumentBody = template.GetContent() as XmlDocument;
// generate doc
}
}
The template would be created elsewhere and injected into the document generator. My question is, is this a good design? I am casting the generic T
into XmlDocument (which is what I need for that specific implementation) and I don’t think that’s the right way to use generics. I’ve always been a little lost when it comes to generics.
Solution
The template related class and interface are looking good to me. But the generator related class and interface are flawed.
Assume one would call the Generate()
method with an ITemplate<string>
which would be totally legal. Sure it would work, but xmlDocumentBody
would be null
.
You could if the passed in template isn’t a ITemplate<XmlDocument>
throw some exception, but a user of this class wouldn’t expect for a generic class which is called correctly to get an exception.
So you should better make the IDocumentGenerator
generic and not the Generate()
method.
public interface IDocumentGenerator<T>
{
void Generate(ITemplate<T> template, string outputDocumentFilepath);
}
and now your WordDocumentGenerator
class should implement IDocumentGenerator<XmlDocument>
like
public class WordDocumentGenerator : IDocumentGenerator<XmlDocument>
{
public void Generate<T>(ITemplate<XmlDocument> template, string outputDocumentFilepath)
{
XmlDocument xmlDocumentBody = template.GetContent();
// generate doc
}
}
For sure if you have different types of templates you should add a DocumentGeneratorFactory
which returns the desired DocumentGenerator
for the task at hand.