Ma fonction de vérification de valeur doit renvoyer à la fois un booléen et un message

14

J'ai une fonction de vérification de valeur, quelque chose comme une fonction de vérification de numéro de carte de crédit, qui est passée dans une chaîne et doit vérifier que la valeur est au bon format.

Si c'est le bon format, il doit retourner vrai.

Si ce n'est pas le bon format, il doit retourner false, et nous dire également ce qui ne va pas avec la valeur.

La question est, quelle est la meilleure façon d'y parvenir?

Voici quelques solutions:

1. Utilisez des codes de retour entiers / énumérés pour signifier les significations:

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];
}

Je n'aime pas cette solution car elle nécessite une implémentation côté appelant.

2. Créez une classe returnCode

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();
}

Cette solution est bien rangée, mais il semble que ce soit exagéré / réinventer la roue.

3. Utilisez des 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();
}

Je suis tenté d'utiliser cette solution, mais on me dit que vous ne devez pas utiliser d'exceptions pour la logique métier.

dwjohnston
la source
«[..] vérifiez que la valeur est au bon format.» Le nom ne devrait-il pas être FormatChecker , alors?
Andy
Le résultat vrai / faux semble redondant. Pourrait-il simplement retourner une chaîne vide ou nulle pour indiquer le succès? Cela fonctionne pour UNIX depuis environ 50 ans. :-)
user949300

Réponses:

14

Utilisez un objet de retour plus complexe qui résume les deux problèmes. Exemple:

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

Cela présente plusieurs avantages:

  1. Renvoie plusieurs éléments de données associés dans un seul objet.
  2. Possibilité d'extension si vous avez besoin d'ajouter des données supplémentaires à l'avenir.
  3. Aucune dépendance au couplage temporel: vous pouvez valider plusieurs entrées et elles n'encombrent pas le message comme dans l'autre réponse. Vous pouvez vérifier les messages dans n'importe quel ordre, même sur les threads.

J'ai déjà utilisé cette conception spécifique auparavant, dans des applications où une validation peut être plus que simplement vraie ou fausse. Un message détaillé est peut-être nécessaire, ou seule une partie de l'entrée n'est pas valide (par exemple, un formulaire à dix éléments peut ne comporter qu'un ou deux champs non valides). En utilisant cette conception, vous pouvez facilement répondre à ces exigences.


la source
Je dois admettre que cette solution est meilleure que la mienne. Le mien n'est pas threadsafe.
Tulains Córdova
@ user61852 Bien qu'il s'agisse d'un aperçu de haut niveau d'une interface pour un objet de résultat, je pense que l'objectif ici est que le code de validation soit son propre objet ne contenant aucun état. Cela le rendrait immuable, ce qui présente de nombreux avantages dont nous parlons encore et encore sur ce site.
Pourquoi une interface est-elle nécessaire?
dwjohnston
1
@dwjohnston une interface n'est pas nécessaire, mais c'est une bonne idée. L'hérédité est un type de couplage très puissant qui ne doit être utilisé qu'en cas de besoin.
Alternativement, vous pouvez simplifier davantage. Le succès n'est pas intéressant, alors déclarez une constante IValidationResult.SUCCESSqui renvoie un message d'erreur vide. Ensuite, votre logique ressemble àif (result != SUCCESS) { doStuff(result.getMessage()); }
Morgen
2

Aucune de ces réponses, utilisez une classe ValueChecker

D'abord une interface pour vous donner de la flexibilité:

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

Ensuite, implémentez autant de vérificateurs de valeur que vous le souhaitez:

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;
    }
}

Exemple de code client:

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());
    }
}

Il a l'avantage que vous pouvez avoir de nombreux contrôleurs de valeur différents.

Tulains Córdova
la source
1
Je ne suis pas sûr que j'aime l'état de conservation du vérificateur de valeur, sans avoir un moyen de voir la dernière valeur vérifiée.
Peter K.
1

Ma réponse étend l'approche de @ Snowman. Fondamentalement, chaque validation, chaque règle métier et chaque logique métier devrait pouvoir donner lieu à une certaine réponse - au moins, dans les applications Web. Cette réponse, à son tour, est affichée pour un appelant. Cela m'a amené à l'interface suivante (c'est php, mais la question est indépendante de la langue):

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

La création d'un opérateur de commutateur agissant comme une expression, et non comme une instruction, conduit à un service d'application ressemblant à ceci:

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)
            ;
    }
}
Zapadlo
la source