What is wrong with this API? Answer
Originally posted at 11/11/2010
The reason that I don’t like this API is that I think it provides a piss poor usability:
In short, it doesn’t provide me with any reasonable way to tell the user why I rejected a message.
Here is the proper way of doing this:
MessageVeto also contains a reason string, which provide a much better experience all around.
Comments
I thought veto is about rejecting something without having to tell why. But maybe I didn't agonize long enough on this line of code...
At first glance I had the same assumption Rafal did - I thought the bool could have simply been a void. Then I figured you'd prefer more context if it actually performed a returnable action on the message.
Coincidently, your answer implies the type returned is an enum. How are you planning to provide a reason string? via some implied annotation?
It can also be a class with static properties like:
public class MessageVeto
{
}
Returning something that can indicate the reason is indeed better. I thought most of the problems / solutions listed in the comments in the last post were good as well.
Funny thing,
plenty of people tried to find a must of having everything strong-typed. Strong-typing is cool, but considering that there is sth wrong when using a primitive type for some result, I don't buy it:P
I like the enum/class with a static property over the boolean value because it is more extensible. Today there may only be two values yes/no, true/false. With the enum you allow for the possibility of additional values without having to change the clients -- unless you really need to .
So, to NOT veto a message, you return a MessageVeto? That's kind of confusing, as well.
I don't get it now.
I agree with Jeff,
MessageVeto.Allowed makes absolutely no sense.
Just the fact you have "Veto.Allowed" read on the same line to express some kind of action... is like saying "this.TempFeels = Hot.Cold".
"Oxymoron: A figure of speech in which incongruous or contradictory terms appear side by side." Indeed.
I think it's better to go with actual your intention is:
EnqueueAction.Allow
EnqueueAction.Veto
Seems better to me.???
I also like:
EnqueueVote.Allow
EnqueueVote.Veto
MessageVote.Allow
MessageVote.Veto
Or something more generic:
QueueVote.Allow
QueueVote.Veto
That's neat. Naming is cool.
How about something like
//MessageAllowed with an internal constructor
MessageAllowed.Yes
MessageAllowed.No
MessageVeto : MessageAllowed {
}
I just think it would be better to use a positive method and a verb that is more common than veto, like:
public virtual MessageAllowed AllowMessage(IncomingMessage msg)
{
}
Thanks to implicit constructors you can refactor the API without any breaking changes at callsites. I had a certain case recently with a constraints callback.
Hey Ayende,
Instead of creating new "result" objects each time I need a method to return a reason, I use the following Result object that I keep in my common library.
It creates a generalized way of solving the problem you are addressing.
I have a blog post written about it here: http://maxoncode.com/ though it is pretty straightforward.
I also use it to encapsulate any exceptions. I know it is somewhat non-standard, but for general shared code I don't necessarily know how the calling code wants to do exception handling, so if something goes awry, I'd just set the Result.Value to false, and populate the Result.Exception property, return the result and let the calling code decide whether it needs to throw or not. If nothing else it makes unit testing the method much cleaner.
public class Result : Result <object,>
{
}
public class Result <t : Result <t,>
{
}
/// <summary
/// Wraps an object to allow a function to return additional data about its execution to the caller
///
{
}
Comment preview