La mia funzione di controllo del valore deve restituire sia un valore booleano che un messaggio


14

Ho una funzione di controllo del valore, qualcosa di molto simile a una funzione di controllo del numero di carta di credito, che viene trasmessa in una stringa e deve verificare che il valore sia del formato corretto.

Se è nel formato giusto, deve tornare vero.

Se non è il formato giusto, deve restituire false e anche dirci cosa non va nel valore.

La domanda è: qual è il modo migliore per raggiungere questo obiettivo?

Ecco alcune soluzioni:

1. Utilizzare i codici di ritorno integer / enum per indicare i significati:

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

Non mi piace questa soluzione in quanto richiede l'implementazione sul lato chiamante delle cose.

2. Creare una 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();
}

Questa soluzione è ordinata, ma sembra eccessivo / reinventare la ruota.

3. Utilizzare le eccezioni.

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

Sono tentato di utilizzare questa soluzione, ma mi viene detto che non dovresti usare eccezioni per la logica aziendale.


'[..] controlla che il valore sia del formato giusto.' Il nome non dovrebbe essere FormatChecker , allora?
Andy,

Il risultato vero / falso sembra ridondante. Potrebbe semplicemente restituire una stringa vuota o nulla per indicare il successo? Funziona con UNIX da circa 50 anni. :-)
user949300,

Risposte:


14

Utilizzare un oggetto di ritorno più complesso che incapsuli entrambi i problemi. Esempio:

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

Questo ha diversi vantaggi:

  1. Restituisce più dati correlati in un oggetto.
  2. Spazio per l'espansione se è necessario aggiungere ulteriori dati in futuro.
  3. Nessun affidamento sull'accoppiamento temporale: è possibile convalidare più input e non ostruiscono il messaggio come nell'altra risposta. Puoi controllare i messaggi in qualsiasi ordine, anche tra i thread.

In realtà ho già usato questo progetto specifico prima, in applicazioni in cui una convalida potrebbe essere più che semplicemente vera o falsa. Forse è necessario un messaggio dettagliato o solo una parte dell'input non è valida (ad esempio un modulo con dieci elementi potrebbe avere solo uno o due campi non validi). Utilizzando questo design, puoi facilmente soddisfare tali requisiti.


Devo ammettere che questa soluzione è migliore della mia. Il mio non è sicuro.
Tulains Córdova,

@ user61852 Mentre questa è una panoramica di alto livello di un'interfaccia per un oggetto risultato, penso che l'obiettivo qui sia che il codice di convalida sia il proprio oggetto senza stato. Ciò lo renderebbe immutabile, il che ha molti vantaggi di cui parliamo più volte su questo sito.

Perché è necessaria un'interfaccia?
dwjohnston

1
@dwjohnston non è necessaria un'interfaccia, ma è una buona idea. L'ereditarietà è un tipo di accoppiamento molto forte che dovrebbe essere usato solo quando necessario.

In alternativa, puoi semplificare ulteriormente. Il successo non è interessante, quindi dichiarare una costante IValidationResult.SUCCESSche restituisce un messaggio di errore vuoto. Quindi la tua logica è simileif (result != SUCCESS) { doStuff(result.getMessage()); }
Morgen,

2

Nessuna delle precedenti, usa una classe ValueChecker

Innanzitutto un'interfaccia per darti flessibilità:

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

Quindi implementa tutti i valutatori di cui hai bisogno:

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

Codice client di esempio:

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

Ha il vantaggio di poter avere molti controllori di valori diversi.


1
Non sono sicuro che mi piace il controllo dello stato mantenendo lo stato, senza avere un modo per vedere l'ultimo valore verificato.
Peter K.

1

La mia risposta estende l'approccio di @ Snowman. Fondamentalmente, ogni convalida, ogni regola aziendale e ogni logica aziendale dovrebbero essere in grado di dare una risposta, almeno nelle applicazioni web. Questa risposta, a sua volta, viene visualizzata a un chiamante. Questo mi ha portato alla seguente interfaccia (è php, ma la domanda è di natura indipendente dal linguaggio):

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

La creazione di un operatore switch che agisce come un'espressione, non come un'istruzione, porta a un servizio dell'applicazione simile a quello:

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)
            ;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.