Простой и интуитивно понятный подход к определению правил в Java

«Все должно быть сделано как можно проще, но не проще». -А. Эйнштейн

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

Поэтому неудивительно, что когда я сталкиваюсь с проблемами, я ищу простые решения. Недавно я столкнулся с проблемой, когда казалось, что нет простого решения: бизнес-правила превращались в проблему обслуживания. Итак, я решил написать свой собственный Java Rules Engine.

Я знаю, что вы думаете. А как насчет Drools? Или даже простые правила? Поверьте, я смотрел на них. И их было больше, чем мне было нужно. Раньше я использовал Drools, и хотя он был очень мощным, я бы не назвал его простым; Мне нужно было простое решение (чтобы избавиться от растущего беспорядка в моем проекте из запутанных бизнес-правил if / then / else), которое было бы легким, легким в освоении и в то же время мощным. Easy Rules был близким соперником, но даже у него была некоторая громоздкость и небольшая особая церемония, на изучение которой моим разработчикам потребовалось время. Войти в свод правил.

RuleBook начинался как простая реализация шаблона Цепочка ответственности с некоторым синтаксическим сахаром, который включал лямбда-выражение и базовый предметно-ориентированный язык задано / когда / затем (DSL). И по своей сути это то, что есть до сих пор! Он делает все, что должен делать механизм правил. Но это просто, практически без обучения (или зависимостей). И его DSL поддерживает лямбда, поэтому правило можно буквально определить в одной строке кода. Как вам такая маленькая церемония!

У слюни по-прежнему есть свое место. И в некоторых случаях это может быть лучший инструмент, чем RuleBook. Но я думаю, что во многих случаях RuleBook - лучший выбор (и не только потому, что я его написал :). RuleBook отдает предпочтение простоте и предсказуемому порядку выполнения превыше всего, тогда как Drools отдает предпочтение гибкости и скорости превыше всего. Конечно, бывают случаи, когда вам нужно написать правила несколькими способами или заставить их выполняться с использованием эффективности алгоритма Rete. Но обычно производительность выполнения и управление источниками правил - не более серьезные проблемы, чем простое определение правил и обеспечение их предсказуемости. Чаще всего простота и предсказуемость важнее чистой производительности; первые нужны всегда, вторые нужны редко.

«Любая система с достаточным количеством правил, требующих сложных алгоритмов для достижения хорошей производительности, вероятно, имеет слишком много правил, чтобы их можно было понять». -M. Фаулер

Самый простой способ определить правила в RuleBook - это класс RuleBook.

В приведенном выше примере определены два факта (данные, передаваемые в правила, которые также могут отражать изменения состояния) и два правила, определенные с помощью RuleBook DSL. Поскольку правила выполняются в том порядке, в котором они определены, здесь нет никаких догадок - то, что вы видите, - это то, что вы получаете, и порядок, в котором вы это получаете. Нет ничего проще.

Я предпочитаю использовать DSL для быстрого определения правил. Но если у вас растущая библиотека правил, RuleBook позволяет вам абстрагировать правила в отдельные классы POJO, как показано ниже.

Правила POJO используют аннотации для сопоставления методов с правилами и фактами книги правил. Но они работают точно так же. Однако вместо определения правил внутри класса RuleBook правила определяются внутри пакета, который сканируется RuleBookRunner, который затем использует правила в пакете для создания RuleBook.

Пример, приведенный непосредственно выше, оценивает соискателей ссуды на соответствие правилам, чтобы определить, одобрены они или нет. Есть 3 правила, которые определяют принятие ссуды: заявителей может быть не более трех, и если у кого-либо из соискателей кредитный рейтинг ниже 700, то у всех соискателей должно быть не менее 50 000 долларов наличными. Хотя это относительно простой пример, быстро становится ясно, что RuleBook может легко масштабировать правила простым способом, сохраняя правила отделенными друг от друга.

Наконец, когда я создавал RuleBook, я заметил, что нужны правила, сводящие факты к единому результату. Как следствие, родился DecisionBook. DecisionBook - это особый тип класса RuleBook, который поддерживает результат отдельно от его фактов (обратите внимание, как это делает и приведенное выше правило POJO). И, как и в случае с фактами, результат может быть прочитан или изменен любым правилом в цепочке, позволяя результату быть совокупностью действий каждого правила в DecisionBook.

То, что изначально начиналось как поиски простого и оптимизированного механизма правил Java, превратилось в проект GitHub. И, как и все лучшие проекты, он возник из-за необходимости решить повторяющуюся проблему и создать лучшее [и более простое] программное обеспечение.

О, и если вы заинтересованы в участии, пожалуйста, сделайте это!

Обновление: с тех пор, как эта статья была написана впервые, в RuleBook была добавлена ​​дополнительная поддержка Spring!