CanHandle – Handle pattern conundrum
When you have multiple classes implementing an IWorker
interface with a CanHandle
and Handle
method, such as
interface IWorker { public Boolean CanHandle(IWork work); public void Handle(IWork work); }
you find yourself in a conundrum: should you call CanHandle
again from Handle
?
If you don’t, someone could pass in work that a worker isn’t designed to handle and that can lead to a multitude of different exceptions or subtle, very hard to debug errors. Avoiding those errors was a vote in favor of calling CanHandle
from Handle
, while the double execution of CanHandle
for well behaved clients of your IWorkers
was a vote against it.
A previous post – Act like a pessimist and call CanHandle from Handle (again)? – discussed a way out of the conundrum by putting the CanHandle
call in an Debug.Assert
statement.
That’s a perfectly valid approach. But… what if you could avoid the conundrum altogether?
Avoiding the conundrum
The problem with a class with one method that validates X, and another method then processes X is that you make that class hugely dependent on its clients and you need to take steps to ensure that people use your class the way it was intended to be used.
Why not make it simpler for everyone involved? Simpler for clients of your workers and simpler for your worker classes themselves.
What is the reason for having a CanHandle
method to begin with?
The answer usually is that 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.
So a client of your workers (usually a work distributor of some sort) would execute a loop like:
ICollection<IWorker> _Workers; public void Distribute(IWork work) { foreach (var worker in _Workers) { if (worker.CanHandle(work)) { worker.Handle(work); break; } } }
But why make a client of your workers pose the question at all? After all, anyone with work looking for a worker to do it, really is only interested in getting their work done.
So … why not simply have them pass the work to the Handle
method directly? All you then need to do is make Handle itself check whether it can actually Handle the work and respond accordingly.
public Boolean Handle(IWork work);
Using this approach the loop in a work distributor would look like:
public void Distribute(IWork work) { foreach (var worker in _Workers) { if (worker.Handle(work)) break; } }
Good. And you can make it even better.
Polishing the alternative
Previously, Handle
would try to execute the work that it received and only fail to do see as a result of errors or exceptions. Now though, when you call Handle
you need to check whether the work was actually accepted for execution. That makes Handle
a bit of a misnomer. Accept
sounds better.
Another peeve with the above method is that a Boolean
result isn’t very descriptive. What does it mean when the method returns false? Was the work not accepted, or was it accepted and was the execution of that work unsuccessful? Personally, I nowadays only use Boolean result values for methods answering a CanXxx, IsXxx, DidXxx, HasXxxx question. In other cases, I prefer a bit more self-documentation and use descriptive enums for parameter and result types. Even if those enums only contain two members.
In this case for example:
enum AcceptWorkResponse { Rejected, Accepted }
This type of return value makes it clear that the work can be rejected. It also conveys that the work has, so far, only been accepted and may not necessarily have been done yet. For example when the work will be executed in a separate thread.
Your Handle
, now named Accept
, method then is declared as:
public AcceptWorkResponse Accept(IWork work);
And the loop becomes:
public void Distribute(IWork work) { foreach (var worker in _Workers) { if (worker.Accept(work) == AcceptWorkResponse.Accepted) break; } }
Yes! Much more self-documenting.
Final touch
As the Accept
method is now itself telling clients of the IWorker
interface whether the work will be done, there is no need to keep CanHandle
around. Doing so would only serve to confuse matters.
If you keep it in a class implementing the IWorker
interface, do reduce its accessibility level to at most protected, preferably to private. After all, whether a class implementing IWorker
can handle some work is now an implementation detail of the Accept
method and doesn’t have to be exposed any further than absolutely necessary.
When you no longer have a CanHandle
method, you can simply write all the tests on the Accept
method. It no longer matters how it determines its answer to whether it will do the work, just as long as it responds appropriately to the work it is handed.
That’s it. Enjoy!
What code had you wondering how to test it? I’d love hearing from you by email or in the comments below. I read everything and will try to help where and as best I can.
Leave a Reply