I write lots of server-side software with XML APIs. I’m constantly taking an XML document as input, maybe transforming it to another format, sending that XML doc somewhere for processing, then returning the resulting XML back to the client. It’s all nice and flexible and loosely coupled, but if all my parameters are of type XmlDocument my code is hard to read, and I may accidentally confuse parameters and still compile just fine. By subclassing XmlDocument and adding no new code, I can get compile-time checks against parameter confusion, better readability, and all the XmlDocument functionality I know and love still works unchanged.
Let’s say my service uses adapters that implement the following interface:
public interface IMyProcessAdapter
{
XmlDocument TransformToMyProcessSubmission(XmlDocument rawInput);
XmlDocument RunMyProcess(XmlDocument myProcessSubmission);
}
I have some app code to execute against an IMyProcessAdapter-implementing class:
function XmlDocument GetProcessResults(IMyProcessAdapter someAdapter, XmlDocument rawInput)
{
XmlDocument myProcessSubmission = someAdapter.TransformToMyProcessSubmission(rawInput);
XmlDocument myProcessResults = someAdapter.RunMyProcess(myProcessSubmission);
return myProcessResults;
}
No problem. Everything compiles and runs just fine. However, the following code also compiles just fine, even though I’ve gotten all my XmlDocument parameters confused:
function XmlDocument GetProcessResults(IMyProcessAdapter someAdapter, XmlDocument rawInput)
{
XmlDocument myProcessSubmission = someAdapter.TransformToMyProcessSubmission(rawInput);
XmlDocument myProcessResults = someAdapter.RunMyProcess(rawInput); // Note how I accidentally pass in the rawInput, not myProcessSubmission
return myProcessSubmission; // And then I pass back the input, not the output!
}
But what if I subclassed XmlDocument for each parameter? I don’t actually add any functionality to these new classes, though of course I could some day. I’m just asking the compiler to ensure that every time I pass an XmlDocument parameter it’s really the one I mean to use. Here’s my new interface declaration with these new subclasses:
public class RawInput : XmlDocument {}
public class ProcessSubmission: XmlDocument {}
public class ProcessResponse: XmlDocument {}
public interface IMyProcessAdapter
{
ProcessSubmission TransformToMyProcessSubmission(RawInput rawInput);
ProcessResults RunMyProcess(ProcessSubmission myProcessSubmission);
}
My code is now easier to read, as its intent is much clearer, and any accidental substitution of a RawInput for a ProcessSubmission will result in a compilation error, not a bug late in testing or production:
function ProcessResponse GetProcessResults(IMyProcessAdapter someAdapter, RawInput rawInput)
{
ProcessSubmission myProcessSubmission = someAdapter.TransformToMyProcessSubmission(rawInput);
ProcessResponse myProcessResults = someAdapter.RunMyProcess(myProcessSubmission);
return myProcessResults;
}
And of course, since all these parameters inherit from XmlDocument, I can call myProcessResults.SelectSingleNode(“//whatever”), myProcessSubmission.LoadXml(“<hi mom/>”), etc. If there’s a downside to this approach, please let me know in the comments, because I can’t find it.