08.08.2021, 14:02 | #1 |
Участник
|
[CodeStyle] check* vs validate* методы. в чем разница в X++? какой будет выводить в инфолог?
любая версия аксапты.
Вопрос относится к стилю кодирования и к вашим предпочтениям. Вопрос и ответы не обязывают никого ничем. Внимание! для других языков есть свои требования и рекомендации для check* и validate* методов. В этой ветке вопрос про X++. Обсуждения других языков допускаются, но хотелось бы услышать именно про X++. в аксапте используются check- и validate- методы. например,
как видим, check- и validate- методы могут быть методами объекта, могут быть статическими, могут выводить сообщения в инфолог, могут не выводить, могут бросать исключения, могут не бросать могут даже создавать диалоговые окна для взаимодействия с пользователями Вопрос: есть ли для вас разница между check- и validate- методами в X++? ожидаете ли вы какие-либо различия в поведении этих методов? а должны быть на ваш взгляд? |
|
08.08.2021, 14:43 | #2 |
Administrator
|
Поскольку вопрос чисто предпочтений, то я (лично для себя) выработал следующие правила (при условии, что нет правил, установленных используемыми фреймворками):
Методы check* отвечают за одну проверку. Методы validate* отвечают за комплекс проверок (т.е. грубо говоря состоят из вызовов методов check*). При этом комплексов может быть много и они могут состоять из разного набора проверок (аналог привилегий и Duty в 2012). Я люблю делать проверочные методы методами объекта (а не статическими), чтобы иметь возможность обращаться к глобальным переменных классам или внутренним private-методам Сообщения, исключения, диалоговые окна - это все регулируется параметрами методов в зависимости от потребности (например, иногда вместо инфолога надо писать в таблицу текст ошибки). Ну и собственно все. Пример (абстрактный). Хочу написать алгоритм разноски для своей условной заявки на покупку. Разноска генерит проводку в ГК и приходную проводку в InventTrans. У меня будет: 1) проверка на открытость периода в ГК 2) проверка на открытость периода по складу (ValueOpen = Да) 3) проверка на наличие товара по необходимым аналитикам 4) проверка на наличие бюджета ... какие-то другие проверки. По бизнесу (к примеру) сначала выполняется проверка на наличие товара. Если он есть, то закупка не выполняется. Если товара нет, то выполняется проверка на наличие бюджета. После успешной проверки бюджета - выполняется разноска по складу и ГК. Я хочу сделать (условно) 4 метода check*, которые в свою очередь будут вызываться в разное время, причем они также могут быть вызваны пользователем по кнопке "Проверить" или "Предварительный просмотр проводок". При этом у меня будут методы validate*, которые будут вызывать эти методы check*: 1) validate на этапе проверки наличия товара 2) validate на этапе проверки бюджета 3) validate на этапе разноски Соответственно - наполнение этих validate-методов может быть разным, при этом, к примеру, проверку на открытость периода я могу вызывать в каждом validate-методе, но сама логика проверки у меня будет в едином методе check
__________________
Возможно сделать все. Вопрос времени Последний раз редактировалось sukhanchik; 08.08.2021 в 14:46. |
|
|
За это сообщение автора поблагодарили: mazzy (5), NetBus (3), dech (5). |
08.08.2021, 15:15 | #3 |
Участник
|
О, интересный подход.
А у тебя check-методы могут вызываться не из validate? И что насчет инфолога? ты допускаешь, что все они могут что-то выводить в инфолог? и что насчет диалога с пользователем? Ну, этот пресловутый Book_RU.validateDelete? |
|
08.08.2021, 19:50 | #4 |
Administrator
|
Конечно. Я же привел пример наличия кнопки "Проверка". validate - это некое событие в алгоритме, по результатам которого отрабатывает основной код (например, разноска).
Цитата:
Цитата:
Вообще - тут сложно вырабатывать какие-то правила - слишком сильно реализация зависит не только от бизнес-задачи, но и от места выполнения проверки. Пример 1. Разноска товарного расхода. Проверка необходимого в наличии количества выполняется до резервирования, но если резервирование уже прошло, то нет смысла проверять необходимое количество в наличии - оно гарантированно будет - в этом смысл резервирования. Пример 2 (AX2012). Проверка достаточности бюджетных средств. Если не используется функционал резервирования бюджета (который привязывает резерв к документу) и разносимый документ не указан в настройках контроля бюджета, то по сути бюджет может "пропасть" в любой момент, а значит его есть смысл регулярно проверять. Пример 3. Аналог примера 2, только речь идет про проверку на закрытость периода. Конечно период могут закрыть в любой момент, но все же нет смысла проверять закрытость периода условно при любом изменении нашей "заявки на покупку", потому что это для пользователя не столь важно, как, к примеру, неожиданная "потеря" бюджета. Я намеренно остаюсь в приведенных ранее мною примерах проверок, чтобы не распыляться. В общем - общая идея такова, что в некотором алгоритме всегда есть проверки, которые можно выполнять на любом этапе алгоритма, а есть проверки, которые есть смысл выполнять только на определенных этапах. Но в любом случае - мне нравится идея выделять всевозможные проверки в методы с префиксом check*, а уже вызывать из методов validate* только те проверки, которые необходимо выполнить именно в данном validate*. Я также понимаю, что одна и та же проверка может быть вызвана как программно несколько раз (из нескольких validate*), так и условно вручную по нажатию специальной кнопки.
__________________
Возможно сделать все. Вопрос времени Последний раз редактировалось sukhanchik; 08.08.2021 в 19:59. |
|
09.08.2021, 09:44 | #5 |
Участник
|
У меня дополнительный вопрос по поводу исключений.
Кто как пользуется методами validate*? Стоит ли в этих методах вызывать исключения? или они только должны возвращать булевый тип и всё? В принципе к check-методам в этом плане я довольно категоричен и использовать здесь исключения себе не позволяю. Однако, что касается validate*, то здесь напрашиваются два шаблона. По факту приходится использовать оба. Первый - возвращает булевый тип и пишет в инфолог неудачные проверки обычно с помощью checkFailed или других check-методов. Например: X++: if (custTable.validateWrite())
{
custTable.insert();
} Пример: X++: public static void validateAdapterClass(classId integrationAdapterClassId) { AifIntegrationAdapter integrationAdapter = AifAdapterManager::getIntegrationAdapter(integrationAdapterClassId); if (AifAdapter::exist(integrationAdapterClassId)) throw error(strfmt("@SYS95137", classId2Name(integrationAdapterClassId))); }
__________________
// no comments |
|
09.08.2021, 12:59 | #6 |
Участник
|
Это уже обсуждается в теме [CodeStyle] методы *noThrow vs *OrThrow vs optional parameter?
|
|
|
За это сообщение автора поблагодарили: dech (2). |
09.08.2021, 13:06 | #7 |
Участник
|
У нас подход практически идентичен тому, который описывает sukhanchik
check* методы проверяют конкретные небольшие бизнес-правила, а validate проверяют процессы. При этом validate вызывают check*. Сами check, как правило, выводят сообщения при отклонениях от допустимых значений. Но, часто в check* есть параметр для работы "втихую" - используется когда проверка бизнес-правила и, например, доступности кнопок в интерфейсе близкая и может использоваться и для "засеривания" кнопок и для проверки при выполнении. Ну а для случаев, когда не нужны сообщения, предпочитаем называть методы не check*, а как-то по другому - is*, has* и т. п. Естественно, check* не обязательно вызываются из validate*. |
|
|
За это сообщение автора поблагодарили: sukhanchik (4), NetBus (3). |
09.08.2021, 17:48 | #8 |
Участник
|
Цитата:
Сообщение от Raven Melancholic
Это уже обсуждается в теме [CodeStyle] методы *noThrow vs *OrThrow vs optional parameter?
__________________
// no comments |
|
10.08.2021, 08:57 | #9 |
Administrator
|
Цитата:
Отсюда вывод, что в методах check* всегда должно быть какое-то осмысленное для пользователя сообщение об ошибке. Например, проверка на закрытость периода, наличие товара / бюджета - пользовательские. А проверка на наличие записи в таблице - не пользовательская. Соответственно, в методы check* я бы убрал пользовательские проверки, а в методы is*, has* - непользовательские. При этом надо понимать, что одна и та же проверка может быть одновременно пользовательской и не пользовательской в зависимости от задачи. Например, я хочу сделать недоступным поле "Группа номенклатурных моделей", если есть проводки по номенклатуре. В этом случае проверка на наличие проводок будет непользовательской (метод hasInventTrans) Но я могу захотеть не делать недоступным поле, а выдать ошибку при попытке его изменения, если уже есть проводки. В этом случае проверка будет пользовательской (метод checkExistsInventTrans) с осмысленной ошибкой "По данной номенклатуре уже есть движения". При этом мне никто не запрещает метод checkExistsInventTrans сделать просто оберткой метода hasInventTrans, но с дополнительным выводом сообщения. Опять-таки - это все красиво и замечательно, пока есть время и возможность "сделать по феншую". Когда сроки уже начинают поджимать или закрываешь какую-то горящую проблему "по-быстрому" то "феншуй" нередко откладывается в сторону, особенно, если его детали приходится вспоминать.
__________________
Возможно сделать все. Вопрос времени |
|
10.08.2021, 09:43 | #10 |
Участник
|
Цитата:
Если класс перегружен методами, то как ты их не называй понять логику будет сложно. У класса должна быть ограниченная зона ответственности и тогда будет меньше проблем с именованием методов. Конечно это полностью не отменяет задачу правильного именования методов, но за счёт более узкого контекста, задаваемого классом, делает эту задачу менее критичной |
|
|
За это сообщение автора поблагодарили: sukhanchik (4). |
10.08.2021, 17:00 | #11 |
Administrator
|
Цитата:
Я в качестве примера приводил операцию разноски, которая заведомо предполагает выполнение большого количества проверок перед выполнением алгоритма. В этом примере класс, отвечающий за взаимодействие с пользователем будет минимален, потому что как такового взаимодействия нет (кнопку нажали и процесс пошел). Если в качестве примера приводить допустим операцию закрытия склада / расчета сводного плана, то да, действительно до запуска процедуры может быть куча параметров в диалоге и действительно будет актуальной задача по обработке взаимодействия с пользователем. Но в целом - я также считаю, что логику взаимодействия с пользователем нужно отделять от внутренней логики, чтобы была возможность программно запустить обработку внутренней логики отбросив логику взаимодействия с пользователем (условно - программно передать процедуре закрытия склада все необходимые параметры и программно ее запустить, не связываясь с обработчиком параметров в диалоге)
__________________
Возможно сделать все. Вопрос времени |
|