Моя функция проверки значения должна возвращать как логическое значение, так и сообщение

14

У меня есть функция проверки значения, что-то очень похожее на функцию проверки номера кредитной карты, которая передается в строке и должна проверить, что значение имеет правильный формат.

Если это правильный формат, он должен вернуть true.

Если это неправильный формат, он должен вернуть false, а также сообщить нам, что не так со значением.

Вопрос в том, как лучше всего этого добиться?

Вот несколько решений:

1. Используйте целочисленные / enum коды возврата для обозначения значений:

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

Мне не нравится это решение, так как оно требует реализации на стороне вызывающего.

2. Создайте класс 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();
}

Это решение опрятно, но кажется, что это перебор / переизобретение колеса.

3. Используйте исключения.

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

Я испытываю желание использовать это решение, но мне сказали, что вы не должны использовать исключения для бизнес-логики.

dwjohnston
источник
'[..] проверьте, что значение имеет правильный формат.' Разве имя не должно быть FormatChecker ?
Энди
Истинный / ложный результат кажется излишним. Может ли он просто вернуть пустую или нулевую строку, чтобы указать успех? Это работало для UNIX около 50 лет. :-)
user949300

Ответы:

14

Используйте более сложный возвращаемый объект, который заключает в себе обе проблемы. Пример:

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

Это имеет несколько преимуществ:

  1. Возвращает несколько связанных частей данных в одном объекте.
  2. Пространство для расширения, если вам нужно добавить дополнительные данные в будущем.
  3. Не полагайтесь на временную связь: вы можете проверить несколько входов, и они не будут загромождать сообщение, как в другом ответе. Вы можете проверять сообщения в любом порядке, даже по темам.

Я на самом деле использовал этот конкретный дизайн раньше, в приложениях, где валидация может быть больше, чем просто истина или ложь. Возможно, требуется подробное сообщение или недопустима только часть ввода (например, форма с десятью элементами может иметь только одно или два недопустимых поля). Используя этот дизайн, вы можете легко удовлетворить эти требования.


источник
Я должен признать, что это решение лучше, чем мое. Мой не безопасен.
Тулаинс Кордова
@ user61852 Хотя это высокоуровневый обзор интерфейса для объекта результата, я думаю, что цель здесь состоит в том, чтобы проверочный код представлял собой собственный объект, не содержащий состояния. Это сделало бы его неизменным, что имеет много преимуществ, о которых мы снова и снова говорим на этом сайте.
Зачем нужен интерфейс?
dwjohnston
1
@dwjohnston интерфейс не требуется, но это хорошая идея. Наследование - это очень сильный тип сцепления, который следует использовать только при необходимости.
Кроме того, вы можете упростить дальше. Успех не интересен, поэтому объявите константу, IValidationResult.SUCCESSкоторая возвращает пустое сообщение об ошибке. Тогда ваша логика выглядит такif (result != SUCCESS) { doStuff(result.getMessage()); }
Морген
2

Ничего из вышеперечисленного, используйте класс ValueChecker

Первый интерфейс, чтобы дать вам гибкость:

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

Затем внедрите столько проверок, сколько вам нужно:

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

Пример кода клиента:

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

Он имеет то преимущество, что вы можете иметь много разных проверок значения.

Тулаинс Кордова
источник
1
Я не уверен, что мне нравится сохранение состояния проверки значения без возможности увидеть последнее проверенное значение.
Питер К.
1

Мой ответ расширяет подход @ Снеговика. По сути, каждая проверка, каждое бизнес-правило и каждая бизнес-логика должны быть в состоянии дать какой-то ответ - по крайней мере, в веб-приложениях. Этот ответ, в свою очередь, отображается для вызывающего абонента. Это привело меня к следующему интерфейсу (это php, но вопрос не зависит от языка):

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

Создание оператора switch, действующего как выражение, а не как оператор, приводит к тому, что служба приложения выглядит следующим образом:

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)
            ;
    }
}
Западло
источник