You have multiple Worker classes that can each handle a one or more types of command. Your IWorker interface offers two methods: CanHandle
and Handle
.
interface IWorker { public Boolean CanHandle(IWork work); public void Handle(IWork work); }
Perfect.
Anyone with work to be done can now find a worker that says “yay, I can handle this” and then pass off the work to that worker.
What if someone doesn’t call CanHandle
first?
But what if someone offering work just throws work at the Handle
method of any Worker they happen to know?
You could face all manner of exceptions when someone sends KitchenWork
to a BathroomWorker
. The BathroomWorker
stands no chance treating KitchenWork
as BathroomWork
. Exceptions could be flying high and low, or worse, you could end up with a very clean instead of a cooked chicken.
Just try to figure that one out when you receive the bug report as you are about to go home…
Act like a pessimist and call CanHandle
from Handle
(again)?
The simplest solution is to call CanHandle
from Handle
and throw a very specific exception when that returns false
.
class BaseWorker : IWorker { public Boolean CanHandle(IWork work) { return false; } public void Handle(IWork work) { if (!CanHandle(work)) throw new CanHandleNotCheckedError("You should call CanHandle before calling Handle"); // ... snip ... } }
The advantages are obvious. When someone blatantly ignores the CanHandle
– Handle
pattern, you now get a very specific exception of the root cause and program execution wouldn’t even get into the territory where other exceptions would be caused.
And punish the good guys with double execution?
The disadvantage is that every polite client of your Workers is punished with double execution of the CanHandle
method.
That’s not very nice.
And it can add up, especially if CanHandle
is a bit more involved than just a type check or two.
So, should you act like a pessimist and call CanHandle
from Handle
again?
If you like to go home earlyish: Yes, but…
Do you like to go home earlyish?
Or do you like being the “hero” and spending hours on some hard to debug errors?
If you are like me and like to go home on time, then yes, you do want to call CanHandle
from Handle
again.
When you can’t enforce that everybody behaves as good guys, it’s the only way to guarantee that the actual “handling” code is protected from being given something it can’t handle.
… you don’t need to punish the good guys
You can still be nice to the good guys too.
The contract for Handle
is violated when someone doesn’t call CanHandle
first. Looking at it like that tells you that you could put the “extra” CanHandle
call in an Assert
statement.
In C# assert statements come in two flavors.
Debug.Assert(CanHandle(work), "You should call CanHandle before calling Handle");
and
Trace.Assert(CanHandle(work), "You should call CanHandle before calling Handle");
The difference is that Debug.Assert
automatically gets thrown out for Release builds, while Trace.Assert
stays in. So in this particular case you want to use the Debug.Assert
version.
Using a Debug.Assert
, the bad guys get slapped with an assertion failure when doing it wrong, while the good guys don’t incur the overhead in release builds.
Sounds like the best of both worlds…
That’s it. Enjoy!
Can you think of another alternative? Let me know in the comments below or by email.
My own alternative will be out in a couple of posts.
Leave a Reply