Another InitImportantThing Approach

I thought of another approach to the InitImportantThing problem that I blogged about yesterday. I think it’s a bit harder to code, but it’s certainly explicit and avoids the magic method that Jon dislikes so much.

The crux of the problem is that ServiceHostBase needs a valid ServiceDescription in order to operate. The WCF team chose to provide said description to ServiceHostBase via the abstract CreateDescription method. But as we saw, ServiceHostBase can’t call CreateDescription from it’s own constructor. So instead, derived classes are forced to call InitializeDescription in their own constructor. Since that call isn’t enforced by the compiler, it’s easy to forget to include it. Since the exception that gets thrown doesn’t really tell you what went wrong, it’s easy to spend hours trying to figure it out.

So here’s a better approach: since the ServiceHostBase needs a valid ServiceDescription in order to operate, why not pass it in as a constructor parameter?

ServiceHostBase has a protected constructor with no parameters. But since it needs you to call InitializeDescription in your derived class constructor, it really needs the ServiceDescription, a collection of ContractDescriptions (also returned from CreateDescription) and a collection of base addresses (passed into InitalizeDescription). If these were parameters on ServiceHostBase’s constructor, it could validate that information directly, without needing abstract or magic methods.

The one problem with this approach is that the creation of a ServiceDescription is non-trivial. ServiceHost’s implementation of CreateDescription generates the ServiceDescription by reflecting over the service type. You still need that code, but now you would call it from the base constructor initializer instead. That means it has to be a static method, but otherwise it would work just fine. Here’s yesterday’s code, updated for this approach:

public abstract class Base
{
    public Base(string importantThing)
    {
        if (string.IsNullOrEmpty(importantThing))
            throw new Exception();

        _importantThing = importantThing;

    }

    private string _importantThing;

    public string ImportantThing  
    {  
        get { return _importantThing; }  
    }
}

public class Derived : Base
{
    private object _data;

    public Derived(DateTime dt) : base(CreateImportantThing(dt))
    {
        _data = dt;
    }

    private static string CreateImportantThing(DateTime dt)
    {
        //this is obviously trivial, but could be much
        //more complicated if need be
        return dt.ToLongDateString();
    }
}

This seems like the best approach to me. You remove the un-obvious magic method call requirement when deriving your own service host while still enforcing the data consistency check in the base class during construction. Best of both worlds, right?

So I wonder why the WCF team didn’t do it this way?

Comments:

"Since the ServiceHostBase needs a valid ServiceDescription in order to operate, why not pass it in as a constructor parameter? " < Sarchasm > You aren't suggesting that programmers should fully initialize their ImportantThings are you ? That's almost as silly as initializing variables in an old C or C++ compiler so when they're first read, Random data isn't already residing in the called memory location! < /Sarchasm > You approach can work as long as your have programmer that are willing to craft and modify code VS most of the current script monkeys I've dealt with who expect the compilers to do everything but collect their checks for them. What I mean by this is : Bad code exists for good things. Copying bad code and modifying it slightly to fit your needs tends to carry the problems instead of offering desired solutions. Lasyness is a good approach for System Administrators who's job is to keep Networks and PCs operating in an operational state ( Because by being lazy , they want to perform as little work as possible. Thusly, for that to happen, They need to measure and set the requirements and impliment the solutions so it only needs to be performed once. ). This is not applicable to programmers who's job is to properly bind and create code. Now, I'm not implying that programmers should re-invent the wheel every time, But at least look and see if all items brought into a program are required and secured to avoid Miss Takes. ;-) Then again, my programming is a little rusty.
"Copying bad code and modifying it slightly to fit your needs tends to carry the problems instead of offering desired solutions." I don't know for sure, but I'd bet that's probably how the ServiceHostBase design got broken in the first place!
So, does this mean that we have put the final nail in the coffin for "Portable , Reusable Code" ? I think not but then again ... These ARE NOT the droids we're looking for ...