Моя функція перевірки значень повинна повертати як булеве, так і повідомлення


14

У мене є функція перевірки цінності, щось подібне до функції перевірки номерів кредитної картки, яка передається в рядку, і потрібно перевірити, чи значення має правильний формат.

Якщо це правильний формат, йому потрібно повернути true.

Якщо це не правильний формат, йому потрібно повернути помилкове значення, а також повідомити нам, що не так зі значенням.

Питання в тому, який найприємніший спосіб досягти цього?

Ось кілька рішень:

1. Використовуйте коди повернення integer / 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();
}

Я спокусився використовувати це рішення, але мені кажуть, що ви не повинні використовувати винятки для ділової логіки.


'[..] перевірте, чи значення має правильний формат.' Тоді чи не повинно бути ім'я FormatChecker ?
Енді

Справжній / хибний результат видається зайвим. Чи може просто повернути порожній або нульовий рядок, щоб вказати на успіх? Це працювало для UNIX близько 50 років. :-)
user949300

Відповіді:


14

Використовуйте складніший об'єкт повернення, який інкапсулює обидві проблеми. Приклад:

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

Це має ряд переваг:

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

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


Треба визнати, що це рішення краще, ніж моє. Шахта не є безпечною ниткою.
Tulains Córdova

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

Створення оператора комутатора, що діє як вираз, а не як оператор, призводить до того, що служба додатків виглядає так:

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)
            ;
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.