If you've spent any time looking at the new Open Data Protocol Client Libraries on CodePlex, you may have run into the internal XmlWrappingReader class. I'll look into why this was a useful thing to have and what important OData processing aspect it helps with in the future, but for today I want to touch a bit on how and why the layering works.
XmlReader is a great class to wrap with, well, another XmlReader. This seems like a pretty obvious statement, but there are ways of designing APIs that make this easier, and other that make this much harder.
In design-pattern speak, the wrapper we are talking about is more like a decorator that simply forwards all calls to another XmlReader instance. One of the things that makes this straightforward is the relatively "flat" nature of the API. When building a wrapper object such as this one, you'll find that complex object models make the "illusion" of speaking with the original XmlReader harder.
Here are three relatively common design choices that make building wrappers harder.
- Properties that have back-references to the original object. A classic example would be trying to wrap an XmlElement class for example, where the first child property needs to return another wrapper, otherwise accessing the Parent would expose the original element rather than the wrapper.
- Events. These are likely to get tricky. You can't forward event handlers to the wrapped class, for example to listen to property changes, because the events will fire with the wrong sender argument. Instead you'd typically have to register the wrapper class to listen, then re-fire the events, possibly updating values as you're going, taking care of timing issues, considering how to deal with exceptions... you see how this gets messy.
- Types that interact with other systems in terms of their actual type, like serialization. Sure, your class WrapperForFoo may inherit from Foo and forward all calls to and internal Foo and no-one in the world might not that it's not the original Foo, but if you need to clone your object or serialize it and then deserialize it, you'll either deserialize the original Foo and lose whatever transparent magic you were doing, or something is going to change quite a bit in the serialization representation.
Thankfully, XmlReader is a simple API that can be easily wrapper, which allows us to overlay behaviors ("decorate it" in design-pattern-speak), and next time we'll see how the Open Data Protocol Client Library puts that to good use.