Введение
Мы часто говорим об алгоритмах как о "сердце" любой сложной системы. В контексте нашей платформы Гриндаты алгоритмы — это не просто код. Представьте, что данные — это деньги или документы клиентов, а алгоритмы — это инструкции и правила, которые позволяют быстро находить, сортировать, обрабатывать и сохранять эти "деньги/документы".
Процесс создания алгоритма для бизнес-логики всегда начинается с декомпозиции действий. Далее мы переводим эти действия на алгоритмический язык, подбирая оптимальные пути решения. Но чтобы делать это быстро и эффективно, необходима прочная концептуальная база.
Формулы удачного написания алгоритма не существует, поскольку задачи всегда разного толка и сложности. Однако понимание основных парадигм программирования — это первый и главный ключ к написанию оптимально работающих алгоритмов. Наши алгоритмы являются мультипарадигменными, поэтому рассмотрим два основных подхода, лежащих в их основе: ООП и ФП.
Концептуальная основа: ООП (Объектно-ориентированное программирование)
Концепция ООП лежит в основе структуры и окружения нашей платформы. Понимание ООП необходимо аналитику для:
Качества обработки требований: Умение выделять сущности, их свойства и связи.
Понимания платформы: Лучшее понимание архитектурных решений, предлагаемых разработчиками.
Проектирования гибких систем: Создание решений, которые легко изменять и переносить.
Рассмотрим ключевые понятия ООП на знакомой банковской аналогии:
-
Класс vs Объект:
Класс — это чертеж, инструкция или шаблон для создания объектов. Аналогия: чистый бланк Кредитной заявки. В шаблоне прописаны пустые поля (ФИО, сумма) и действия/методы ("отправить на проверку", "одобрить").
Объект — это уже конкретная сущность, созданная по шаблону-классу, с реальными данными. Аналогия: заполненный бланк "кредитнаяЗаявка_Иванова" на 500 000 руб..
Интерфейс: Более абстрактное понятие, которое описывает, что объект должен делать, но не описывает, как он это будет делать. Аналогия: Банковское отделение. Интерфейс предоставляет набор услуг ("открыть счет", "выдать наличные"). Реализация может быть разной: центральный офис, дополнительный офис или банкомат. Интерфейс позволяет разным объектам выполнять один и тот же набор действий.
Принципы ООП
Абстракция: Создание простой "модели" сложного предмета, содержащей только самые важные детали для решаемой задачи. Аналогия: форма Заявления на кредит. Вам не нужно знать все процессы скоринга; вы видите только поля (ФИО, доход) — это абстракция всего кредитного процесса.
Инкапсуляция: Объединение данных и методов работы с ними в одной "капсуле" (объекте) с контролируемым доступом. Внутренняя "кухня" скрыта. Аналогия: Банкомат. Вы взаимодействуете через строгий интерфейс (вставили карту, ввели PIN) и не знаете, как он внутри пересчитывает купюры и связывается с сервером. Ваши данные защищены.
Наследование: Создание нового объекта на основе существующего, перенимая его свойства и поведение. Аналогия: банковские продукты. Базовый продукт "Счёт" (номер, баланс). На его основе создается "Депозит", который наследует свойства Счета, но добавляет свои (процентная ставка, срок окончания).
Полиморфизм: Возможность использовать одно и то же действие для работы с разными типами объектов. Аналогия: "Рассчитать проценты". Для "Депозита" расчет происходит по одним правилам (конец срока), а для "Кредитной карты" — по другим (ежемесячно, на остаток долга). Для пользователя это одно действие, но внутри система выполняет разные алгоритмы.
Концептуальная основа: ФП (Функциональное программирование)
Хотя наше окружение основано на ООП, сами алгоритмы часто используют подходы Функционального программирования (ФП), которое обеспечивает предсказуемость и надежность кода. В ФП программа — это набор функций, преобразующих входные данные в выходные. Ключевая идея: функции должны быть похожи на математические, обеспечивая идемпотентность.
Понимание ФП выгодно аналитику, так как:
Помогает формализовать бизнес-правила в виде чистых, атомарных требований, которые легко тестировать.
Способствует описанию бизнес-процессов как цепочки преобразований данных (Data Flow), что делает спецификации четкими.
Ключевые принципы ФП
Неизменяемость данных (Иммутабельность): Исходные данные не изменяются. Для внесения изменений создается новая версия. Аналогия: ошибочная транзакция. Вместо удаления или изменения ошибочной операции, создается новая транзакция на ту же сумму со ссылкой на исходную. Это сохраняет всю историю произошедших событий.
Чистые функции (Идемпотентные): При одних и тех же входных данных, всегда возвращают один и тот же результат и никак не влияют на работу системы (не меняют глобальные переменные, не пишут в лог). Они ТОЛЬКО ВЫЧИСЛЯЮТ. Пример: Функция рассчитатьПроцент(сумма, ставка, срок) всегда вернет одинаковое значение, независимо от того, кто и когда ее запустит..
Функции первого класса: Функции, которые могут выступать в качестве аргумента для других функций. Пример: Функция обработатьСписокКлиентов принимает в качестве аргумента список клиентов и другую функцию, например, ОтправитьEmail или ОтправитьSMS, и применяет ее ко всему списку. Это повышает эффективность.
Отсутствие побочных эффектов: Функция не должна менять что-либо за своими пределами, кроме своего явного предназначения. Пример: Функция проверитьВозможностьКредита должна только проанализировать доход и кредитную историю, и вернуть ответ: "одобрено" или "отклонено". Она НЕ ДОЛЖНА самостоятельно менять статус заявки в БД или отправлять уведомление.
Алгоритмы платформы: "Хорошие" и "Плохие" практики
Алгоритмы в нашей платформе исходно на уровне back-end написаны на языке Groovy. При этом они являются мультипарадигменными: ООП учит нас структурировать окружение (объекты, таблицы), а ФП приучает правильно использовать функции внутри алгоритма.
Качество работы алгоритма, его скорость и понятность напрямую зависят от окружающих настроек и того, как написан сам алгоритм. Плохо спроектированная объектная модель (дублирующие справочники, нелогичные связи) приведет к тому, что даже хороший алгоритм будет работать плохо, требуя "костылей".
Ниже приведен список практик, которые помогут вам писать "хорошие" алгоритмы.
Список, дающий "минус-вайб" вашему алгоритму
Избегайте этих распространенных ошибок, которые ведут к медленной работе и сложному сопровождению:
Чрезмерное использование SQL. Стремитесь минимизировать SQL внутри алгоритма.
Использование функции Objects по таблице, где сотни тысяч или миллионы экземпляров.
Поиск не по индексированным полям в функции filterAlg.
Втюхивать в один алгоритм всю логику.
Составлять алгоритм из бесконечной вложенности других алгоритмов (алгоритм → вложенный алгоритм → ещё один вложенный).
Не пользоваться специализированными функциями, например, для расчета вычислимых показателей или при работе с лимитами.
Не тестировать свои алгоритмы самостоятельно. 5 секунд на тест сэкономят часы срочных работ на продакшене.
Доверять написание кода ИИ. ИИ не знает внутренних ограничений нашей платформы и использует избыточные конструкции, которые не подходят для Groovy в Гриндате.
Как писать "правильные" алгоритмы
Комментарии и осмысленное наименование: Комментарии и логичные имена переменных позволяют вам и вашим коллегам быстро вникнуть в суть происходящего, особенно в сложных конструкциях.
Используйте Структуры данных: Структуры данных (массив, хэш, очередь, стек) позволяют зафиксировать набор элементов в оперативной памяти и значительно сократить время работы алгоритма. Например, они незаменимы при работе с ETL.
Логически дробите алгоритм: Дробление важно и необходимо, но должно быть логически обосновано. Вместо одного огромного алгоритма создавайте несколько маленьких, например, используя вложенный алгоритм в разных местах.
Экранирование данных: При работе с интеграциями, JSON, XML или формировании, например, гиперссылки в SendPopUpMessage, важно экранировать данные (например, с помощью функции text для HTML), чтобы избежать ошибок и чтобы front-end "не съел" теги.
Учитывайте строгую типизацию данных.
valerychesnokov
Доброго дня. Скажите, пожалуйста, чем применение SQL такую немилость вызвало? Ведь грамотно написанный СКЛ источник отработает быстрее, чем в цикле крутить объекты с фильтрами. Особенно если в СКЛ несколько таблиц соединено сложной логикой.
Можно как-то раскрыть эту мысль на примере?
П.С: с платформой GD знаком 3.5 года.