Minha função do verificador de valor precisa retornar um valor booleano e uma mensagem


14

Eu tenho uma função de verificação de valor, algo semelhante a uma função de verificação de número de cartão de crédito, que é passada em uma string e precisa verificar se o valor está no formato correto.

Se é o formato certo, ele precisa retornar true.

Se não for o formato correto, ele precisará retornar false e também nos informar o que há de errado com o valor.

A questão é: qual é a melhor maneira de conseguir isso?

Aqui estão algumas soluções:

1. Use códigos de retorno inteiro / enum para significar significados:

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

Não gosto dessa solução, pois requer implementação no lado do chamador.

2. Crie uma 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();
}

Esta solução é arrumada, mas parece exagerar / reinventar a roda.

3. Use exceções.

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

Estou tentado a usar esta solução, mas me disseram que você não deve usar exceções para a lógica de negócios.


'[..] verifique se o valor está no formato correto.' O nome não deveria ser FormatChecker , então?
Andy

O resultado verdadeiro / falso parece redundante. Poderia simplesmente retornar uma String vazia ou nula para indicar sucesso? Isso funciona para o UNIX há cerca de 50 anos. :-)
user949300 30/11

Respostas:


14

Use um objeto de retorno mais complexo que encapsule as duas preocupações. Exemplo:

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

Isso tem várias vantagens:

  1. Retorna várias partes de dados relacionadas em um objeto.
  2. Espaço para expansão, se você precisar adicionar dados adicionais no futuro.
  3. Sem dependência do acoplamento temporal: você pode validar várias entradas e elas não atrapalham a mensagem como na outra resposta. Você pode verificar as mensagens em qualquer ordem, inclusive nos threads.

Na verdade, já usei esse design específico em aplicativos em que uma validação pode ser mais do que simplesmente verdadeira ou falsa. Talvez seja necessária uma mensagem detalhada ou apenas parte da entrada seja inválida (por exemplo, um formulário com dez elementos pode ter apenas um ou dois campos inválidos). Usando esse design, você pode acomodar facilmente esses requisitos.


Devo admitir que esta solução é melhor que a minha. O meu não é seguro.
Tulains Córdova

@ user61852 Embora esta seja uma visão geral de alto nível de uma interface para um objeto de resultado, acho que o objetivo aqui é que o código de validação seja seu próprio objeto sem estado. Isso o tornaria imutável, com muitos benefícios sobre os quais falamos várias vezes neste site.

Por que uma interface é necessária?
precisa saber é o seguinte

1
@dwjohnston uma interface não é necessária, mas é uma boa ideia. A herança é um tipo muito forte de acoplamento que só deve ser usado quando necessário.

Como alternativa, você pode simplificar ainda mais. O sucesso não é interessante, portanto, declare uma constante IValidationResult.SUCCESSque retorne uma mensagem de erro vazia. Então sua lógica é parecida comif (result != SUCCESS) { doStuff(result.getMessage()); }
Morgen

2

Nenhuma das opções acima, use uma classe ValueChecker

Primeiro uma interface para oferecer flexibilidade:

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

Em seguida, implemente quantos avaliadores de valor forem necessários:

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

Código de cliente de amostra:

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

Tem a vantagem de poder ter muitos verificadores de valores diferentes.


1
Não sei se gosto do estado de manutenção do verificador de valores, sem ter como ver o último valor verificado.
Peter K.

1

Minha resposta estende a abordagem de @ Snowman. Basicamente, toda validação, toda regra de negócios e toda lógica de negócios devem resultar em alguma resposta - pelo menos em aplicativos da web. Essa resposta, por sua vez, é exibida para um chamador. Isso me levou à seguinte interface (é php, mas a questão é agnóstica por natureza):

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

A criação do operador do switch agindo como uma expressão, não como uma instrução, leva ao serviço de aplicativo assim:

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)
            ;
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.