La función de mi verificador de valor debe devolver un booleano y un mensaje

14

Tengo una función de verificación de valor, algo así como una función de verificación de número de tarjeta de crédito, que se pasa en una cadena y necesita verificar que el valor tenga el formato correcto.

Si es el formato correcto, debe devolver verdadero.

Si no es el formato correcto, debe devolver falso y también decirnos qué tiene de malo el valor.

La pregunta es, ¿cuál es la mejor manera de lograr esto?

Aquí hay algunas soluciones:

1. Use códigos de retorno de enteros / enumeraciones 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];
}

No me gusta esta solución, ya que requiere implementación en el lado de la persona que llama.

2. Crear una clase 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 solución es ordenada, pero parece excesivo / reinventar la rueda.

3. Use excepciones.

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

Estoy tentado a usar esta solución, pero me dijeron que no debe usar excepciones para la lógica de negocios.

dwjohnston
fuente
'[..] verifique que el valor tenga el formato correcto.' ¿No debería ser el nombre FormatChecker , entonces?
Andy
El resultado verdadero / falso parece redundante. ¿Podría simplemente devolver una cadena vacía o nula para indicar el éxito? Eso funcionó para UNIX durante unos 50 años. :-)
user949300

Respuestas:

14

Utilice un objeto de retorno más complejo que encapsule ambas preocupaciones. Ejemplo:

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

Esto tiene varias ventajas:

  1. Devuelve múltiples datos relacionados en un objeto.
  2. Espacio para la expansión si necesita agregar datos adicionales en el futuro.
  3. No dependa del acoplamiento temporal: puede validar múltiples entradas y no bloquear el mensaje como en la otra respuesta. Puede verificar los mensajes en cualquier orden, incluso a través de hilos.

De hecho, he usado este diseño específico antes, en aplicaciones donde una validación puede ser más que simple o verdadera. Quizás sea necesario un mensaje detallado, o solo una parte de la entrada no sea válida (por ejemplo, un formulario con diez elementos podría tener solo uno o dos campos no válidos). Con este diseño, puede acomodar fácilmente esos requisitos.


fuente
Debo admitir que esta solución es mejor que la mía. El mío no es seguro.
Tulains Córdova
@ user61852 Si bien esta es una descripción general de alto nivel de una interfaz para un objeto de resultado, creo que el objetivo aquí es que el código de validación sea su propio objeto que no contenga estado. Eso lo haría inmutable, lo que tiene muchos beneficios de los que hablamos una y otra vez en este sitio.
¿Por qué es necesaria una interfaz?
dwjohnston
1
@dwjohnston no es necesaria una interfaz, pero es una buena idea. La herencia es un tipo de acoplamiento muy fuerte que solo debe usarse cuando sea necesario.
Alternativamente, puede simplificar aún más. El éxito no es interesante, así que declare una constante IValidationResult.SUCCESSque devuelva un mensaje de error vacío. Entonces su lógica se parece aif (result != SUCCESS) { doStuff(result.getMessage()); }
Morgen
2

Ninguno de los anteriores, use una clase ValueChecker

Primero una interfaz para darle flexibilidad:

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

Luego implemente tantos comprobadores de valor como necesite:

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 muestra:

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

Tiene la ventaja de que puede tener muchos correctores de valor diferentes.

Tulains Córdova
fuente
1
No estoy seguro de que me guste el estado de mantenimiento del verificador de valor, sin tener una forma de ver el último valor verificado.
Peter K.
1

Mi respuesta extiende el enfoque de @ Snowman. Básicamente, cada validación, cada regla de negocio y cada lógica de negocio deberían poder dar alguna respuesta, al menos, en aplicaciones web. Esta respuesta, a su vez, se muestra a la persona que llama. Esto me trajo a la siguiente interfaz (es php, pero la pregunta es de naturaleza independiente del lenguaje):

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

La creación de un operador de conmutación que actúa como una expresión, no como una declaración, conduce al servicio de la aplicación de la siguiente manera:

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
fuente