Indirect implementation is something I don’t see done enough.
I think the biggest reason is “Why would I do that?”
Developers (myself included) that understand interfaces come up with cool reasons like: “So I can swap out the implementation with something else later”. Then we go through and instantiate the class that implements the interface everywhere. Making it catastrophic to remove the implementation later. The developer that ignored the interfaces is probably tasked to help you undo your “extensibility”, further convincing her to not use interfaces.
My most recent task is to make my application “.NET 2.0 compatible”. What does this mean? Means if a feature uses part of a .NET framework that was implemented after .NET 2.0 I should turn off the feature and display a message to the user.
Now, the trick is how do I know there is a problem?
If I make my application depend on System.ServiceModel, and that assembly isn’t present on the users system my app will fail to run entirely. Instead I create a separate project that holds a reference to my application.
So instead of:
This is odd initially, and in visual studio can be a pain in the ass for build ordering. Typically I’ll setup like this:
MyApplication—references -–>MyLibrary<—referenced by—MyImplementation
Now in visual studio, I make MyImplementation a “Project Dependency” to MyApplication. (Note the white checkbox, references are the grey checkboxes that are only removed by removing the project reference)
Plus add a Post-Build script to copy the build output from MyImplementation to the bin directory of MyApplication.
So, neither MyApplication nor MyLibrary are explicitly dependent on MyImplementation. If that assembly fails to load due to missing references I can have a recovery plan.
That takes care of the project setup. Now for how to implement this in code. This is where the factory pattern comes into play.
You can see in the factory code, first I try to get the type from inside the MyImplementation assembly. If that fails (returns null), then I just instantiate a default implementation.
Now, lets say the MyImplementation project referenced a 3rd party assembly that may or may not be on the users system. No worries, our FallbackImplementation can help us recover gracefully when our MyImplementation assembly fails to load due to missing dependencies.
Checkout coderjoe.codeplex.com for a sample solution showing this code in action.