Creating a Generic Template Interface/Class and injecting into another object

Posted on

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.

Leave a Reply

Your email address will not be published. Required fields are marked *