16

I have a value checking function, something much like a credit card number checking function, that is passed in a string, and needs to check that the value is of the right format.

If it's the right format it needs to return true.

If it's not the right format it needs to return false, and also tell us what's wrong with the value.

The question is, what's the nicest way to achieve this?

Here's a few solutions:

1. Use integer/enum return codes to signify meanings:

String[] returnCodeLookup = 
[
"Value contains wrong number of characters, should contain 10 characters",
"Value should end with 1", 
"Value should be a multiple of 3"
]

private int valueChecker(String value)
{
    /*check value*/
    return returnCode;
}

rc = checkValue(valueToBeChecked);
if rc == 0
{
    /*continue as normal*/
}
else
{
    print("Invalid value format: ") + returnCodeLookup[rc];
}

I don't like this solution as it it requires implementation on the caller side of things.

2. Create a returnCode class

Class ReturnCode()
{
    private boolean success;
    private String message;

    public boolean getSuccess()
    {
        return this.success;
    }

    public String getMessage()
    {
        return this.message; 
    }
}

private ReturnCode valueChecker(String value)
{
    /*check value*/
    return returnCode;
}

rc = checkValue(valueToBeChecked);
if rc.getSuccess()
{
    /*continue as normal*/
}
else
{
    print("Invalid value format: ") + rc.getMessage();
}

This solution is tidy, but it seems like overkill/reinventing the wheel.

3. Use exceptions.

private boolean valueChecker(String value)
{
    if int(value)%3 != 0 throw InvalidFormatException("Value should be a multiple of 3";
    /*etc*/
    return True;
}

try {
rc = checkValue(valueToBeChecked);
}

catch (InvalidFormatException e)
{
     print e.toString();
}

I'm tempted to use this solution, but I'm told that you shouldn't use exceptions for business logic.

2
  • '[..] check that the value is of the right format.' Shouldn't the name be FormatChecker, then?
    – Andy
    Commented Feb 23, 2015 at 9:36
  • The true/false result seems redundant. Could it simply return an empty or null String to indicate success? That's worked for UNIX for about 50 years. :-)
    – user949300
    Commented Nov 30, 2017 at 16:51

3 Answers 3

16

Use a more complex return object that encapsulates both concerns. Example:

public interface IValidationResult {
  boolean isSuccess();
  String getMessage();
}

This has several advantages:

  1. Returns multiple related pieces of data in one object.
  2. Room for expansion if you need to add additional data in the future.
  3. No reliance on temporal coupling: you can validate multiple inputs and they do not clobber the message as in the other answer. You can check messages in any order, across threads even.

I have actually used this specific design before, in applications where a validation may be more than simply true or false. Perhaps a detailed message is necessary, or only part of the input is invalid (e.g. a form with ten elements might only have one or two invalid fields). Using this design, you can easily accommodate those requirements.

9
  • I must admit this solution is better than mine. Mine is not threadsafe. Commented Feb 23, 2015 at 13:33
  • @user61852 While this is a high level overview of an interface for a result object, I think the goal here is that the validation code would be its own object containing no state. That would make it immutable, which has many benefits that we talk about over and over on this site.
    – user22815
    Commented Feb 23, 2015 at 15:01
  • Why is an interface necessary?
    – dwjohnston
    Commented Mar 8, 2015 at 22:30
  • 1
    @dwjohnston an interface is not necessary, but it is a good idea. Inheritance is a very strong type of coupling that should only be used when necessary.
    – user22815
    Commented Mar 9, 2015 at 1:02
  • Alternately, you can simplify further. Success is not interesting, so declare a constant IValidationResult.SUCCESS that returns an empty error message. Then your logic looks like if (result != SUCCESS) { doStuff(result.getMessage()); }
    – Morgen
    Commented Jul 13, 2015 at 16:53
2

None of the above, use a ValueChecker class

First an interface to give you flexibility:

public interface IValueChecker {
    public boolean checkValue(String value);
    public String getLastMessage();
}

Then implement as many valuecheckers as you need:

public class MyVeryEspecificValueChecker implements IValueChecker {
    private String lastMessage="";
    @Override
    public boolean checkValue(String value) {
        boolean valid=false;
        // perform check, updates "valid" and "lastMessage"
        return valid;
    }
    @Override
    public String getLastMessage() {
        return lastMessage;
    }
}

Sample client code:

public class TestValueChecker {
    public static void main(String[] args) {
        String valueToCheck="213123-YUYAS-27163-10";
        IValueChecker vc = new MyVeryEspecificValueChecker();
        vc.checkValue(valueToCheck);
        System.out.println(vc.getLastMessage());
    }
}

It has the advantage that you can have many different value checkers.

1
  • 1
    I'm not sure I like the value checker keeping state, without having a way to see the last value checked.
    – Peter K.
    Commented Feb 23, 2015 at 2:40
1

My answer extends @Snowman's approach. Basically, every validation, every business rule and every business-logic should be able to result in some response -- at least, in web applications. This response, in turn, is displayed to a caller. This brought me to the following interface (it's php, but the question is language-agnostic in nature):

interface Action
{
    /**
     * @param Request $request
     * @throws RuntimeException
     * @return Response
     */
    public function act(Request $request);
}

Creating switch operator acting like a expression, not like statement, leads to application service looking like that:

class MyApplicationService implements Action
{
    private $dataStorage;

    public function __construct(UserDataStorage $dataStorage)
    {
        $this->dataStorage = $dataStorage;
    }

    public function act(Request $request)
    {
        return
            (new _SwitchTrue(
                new _Case(
                    new EmailIsInvalid(),
                    new EmailIsInvalidResponse()
                ),
                new _Case(
                    new PasswordIsInvalid(),
                    new PasswordIsInvalidResponse()
                ),
                new _Case(
                    new EmailAlreadyRegistered($this->dataStorage),
                    new EmailAlreadyRegisteredResponse()
                ),
                new _Default(
                    new class implements Action
                    {
                        public function act(Request $request)
                        {
                            // business logic goes here

                            return new UserRegisteredResponse();
                        }
                    }
                )
            ))
                ->act($request)
            ;
    }
}

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