1

I've got a validation function something like this (pseudo-code):

public function validate(Thing thing) {
    if (thing.a != "a") {
        return false; // most of the time
    }
    if (thing.b != "b") {
        return false; // most of the time
    }
    return true;
}

This function is in a domain service class. It's called from lots of controller classes. 90% of the time, all I want is a boolean answer to "is thing valid?" But 10% of the time, I want to display a message on the UI as to why it's invalid, if so. So instead of false when thing.a != "a" occurs, I want a string that says "thing's a is not a" or whatever.

I can think of several ways to manage this, but none of them make me particularly happy.

  1. Just return the string all the time and in the true case just return ok or something similar, but that feels fragile. It also makes the calling code have to do a string compare everywhere.
  2. Return an enumerated type, and have a helper method available to translate the enum into the string for when it's needed, but that feels like I've just hidden the problem. And the calling code has to check a constant -- better than 1, but not by much.
  3. Store the last message in a class property, to be obtained via a getter after a call to validate returns a false. That makes most of my calling code much cleaner since it only deals with boolean results except when it needs to know the cause. But is this fragile? It's a web app, so it's single threaded per request, so race conditions shouldn't be a problem.

I feel like this is a common enough problem that there should be an elegant design pattern for it, but I've been unable to find it.

4
  • 1
    The "elegant design pattern" is the "either" monad aka the "except" monad. If you do searches for "either monad" with your language of choice you may find blog posts that explain it - and how to do it idiomatically in your language - and libraries that give you the class you need to implement it. Now, once you actually see this "elegant design pattern' in use ... you may not want to use it anymore ... But you're specifically looking for elegant - and the "either" monad - which leads you to the entire world of monadification - is nothing if not elegant.
    – davidbak
    Commented Feb 18, 2021 at 18:49
  • 1
    @davidbak Yeah, a functional programming-style monad did occur to me! But really the rest of the code base is very traditional OO, and it's probably a good idea to stay consistent with that.
    – CXJ
    Commented Feb 18, 2021 at 20:56
  • 1
    After rereading your question I'd go with a variation of #3 - but not a class property, just a thread property, and not a single value, but a stack or list of them. Push stuff on the "error message stack" as you're going. Put entire stack traces on it if you wish. Then when you are done with that request and about to return you have the entire history of badness available to format and return - possibly as an header value ("out of band" for the request/response but still available for your logs, and for the client if it fetches it out of the request.
    – davidbak
    Commented Feb 18, 2021 at 21:01
  • 1
    Assuming you can pass functions around, you could do validate(Thing thing, Func onError), where onError is an error handler with the signature onError(error) or onError(thing, error). This lets you de-emphasize (or separate out) the error path in client code. Alternatively, you could do validate(onOK, onError), but depending on the language, this might be somewhat clunky. Another option is to return a response object as suggested in the answer below. Commented Feb 18, 2021 at 21:01

2 Answers 2

0

As you're apparently in a transactional situation - request/response (not ACID) - then consider:

Go with a variation of #3 - but not a class property, just a thread property, and not a single value, but a stack or list of them. Push stuff on the "error message stack" as you're going. Put entire stack traces on it if you wish. Then when you are done with that request and about to return you have the entire history of badness available to format and return - possibly as an header value ("out of band" for the request/response but still available for your logs, and for the client if it fetches it out of the request).

2
  • 1
    That's a clever idea, but I suspect it's both overkill for my situation and would involve adding some complexity to the framework in use.
    – CXJ
    Commented Feb 18, 2021 at 21:47
  • I think I'm going to accept this answer as it is the one which most crystalized my thinking. I'll actually probably just go with a response object suggested in the other (extant) answer, but I can't mark 2 answers as accepted.
    – CXJ
    Commented Feb 18, 2021 at 21:48
4

What about a response object, that contains a boolean indicating validity, and an optional reason string? This way you don't need to worry about looking up enum codes, because the reason is contained within the response object.

public Response validate(Thing thing) {
    Response r = new Response();
    if (thing.a != "a") {
        r.valid = false; // most of the time
        r.reason = "the thing.a is not an 'a'!"
    }
    else if (thing.b != "b") {
        r.valid = false; // most of the time
        r.reason = "the thing.b is not an 'b'!"
    }
    else {
        r.valid = true;
    }
    return r;
}
2
  • I just keep thinking everyone else is more knowledgeable than I am. This will surely work fine. But can I do better? Maybe not, and I'll eventually be ok with that. ;-)
    – CXJ
    Commented Feb 18, 2021 at 20:58
  • 3
    Keep in mind that this amounts to an error code. Which is fine. Just don't mix it with a codebase that uses exceptions. That's just too hard on the brain. Commented Feb 18, 2021 at 21:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.