Модель принятия решений на основе динамических условий - PullRequest
0 голосов
/ 03 ноября 2018

У меня есть xml-файл, который содержит условия, которые нужно проверить по входным данным JSON метаданных метаданных JSON *1004*.

Пример ввода метаданных JSON может быть

{
"max_cash":789,
"min_cash":12,
"start_date":"28 Oct 2018",
"end_date":"02 Dec 2018",
"allowed_cities":"3,4,5,6"
} 

Пример значения данных JSON может быть

{
"cash":34,
"order_date":"03 Nov 2018",
"city_id":"5,4"
}

Каждое условие может формировать выражение и имеет следующий атрибут

  • param_holder - это левый член выражения. Это просто ключ, значение которого находится в метаданных JSON.
  • param_value_holder - это правильный термин выражения. Это просто ключ, значение которого находится в данных значения JSON.
  • оператор - какой оператор будет выполняться слева и справа. Например, <,>, <=,> =, <>, ==, CONTAINS, NOT_CONTAINS, CONTAINS_AT_LEAST_ONE и т. Д.
  • param_type - тип данных левого и правого члена. Например, НОМЕР, ДАТА, ВРЕМЯ, СТРОКА, СПИСОК.

Пример XML выглядит следующим образом

<condtion id="1" param_type="NUMBER" param_holder="max_cash" operator=">=" param_value_holder="cash" next_condition="2"></condtion>
<condtion id="2" param_type="NUMBER" param_holder="min_cash" operator="<=" param_value_holder="cash" next_condition="3"></condtion>
<condtion id="3" param_type="DATE" param_holder="start_date" operator="<=" param_value_holder="order_date" next_condition="4"></condtion>
<condtion id="4" param_type="DATE" param_holder="end_date" operator=">=" param_value_holder="order_date" next_condition="5"></condtion>
<condtion id="5" param_type="LIST" param_holder="allowed_cities" operator="CONTAINS" param_value_holder="city_id" next_condition="-1">

Я работаю с JAVA, и почти все здесь является динамическим, который можно добавлять, изменять и удалять в любое время. В будущем может появиться еще несколько операторов, типов данных и т. Д. Я ищу решение, в котором люди могут добавлять любые условия без повторного развертывания кода. Развертывание кода следует выполнять только в случае изменения метаданных JSON. Я читал об ANTLR, и он думает, что он перестанет убивать решение, а создание и поддержание грамматики и парсера - это накладные расходы для разработчика. Я придумал другое решение с Factory Pattern, где Operator Factory принимает входные данные как оператор и возвращает соответствующий объект Operator, который дополнительно принимает левый член, правый член и тип параметра в качестве параметра, и оценивает условие на основе определенного типа параметра.

public abstract class Operator {

    public abstract boolean evaluate(String leftTerm, String rightTerm, String param_type) throws ParseException;

}

public class GTEOperator extends Operator {
    @Override
    public boolean evaluate(String leftTerm, String rightTerm, String param_type) throws ParseException {
        switch (param_type)
        {
            case "DATE":
                return new SimpleDateFormat().parse(leftTerm).after(new SimpleDateFormat().parse(rightTerm));
            case "NUMBER":
                return Integer.parseInt(leftTerm) >=Integer.parseInt(rightTerm);
            default:
                return true;
        }
    }
}

public class ContainsOperator extends Operator {
    @Override
    public boolean evaluate(String leftTerm, String rightTerm, String param_type) throws ParseException {
        return Arrays.asList(leftTerm.split(",")).contains(rightTerm);
    }
} 

Это решение не является динамическим и требует много переписывания кода. Может ли кто-нибудь помочь мне с лучшим, расширяемым и объектно-ориентированным подходом.

1 Ответ

0 голосов
/ 05 ноября 2018

Я бы предложил здесь несколько более мудрых функциональных подходов.

Прежде всего, избавьтесь от next_condition. Поскольку присоединение коммутативно (т.е. booleanValue1 && boleanValue2 всегда будет равно booleanValue2 && booleanValue1), вам просто наплевать на определенный порядок оценки .

Во-вторых, вам лучше сопоставить каждое условие с предикатом .. Функция, принимающая ваш проверяемый объект и возвращающая одиночный true/false в простейшем случае или что-то более сложное (например, массив значимых ошибок и все, что вам может потребоваться).

Пока у вас есть эти предикаты, вы можете объединять (уменьшать, сворачивать, агрегировать - есть много-много имен для по сути одной и той же функции) их в один: решайте, предпочитаете ли вы ленивый или стремитесь оценить стратегию и либо создайте еще один предикат, либо вычислите их на лету и выведите полученное логическое значение. Таким образом, ваш «клиентский код» получает одну ToBeValidateObject -> Boolean функцию, , прекрасно инкапсулирующую все базовые детали .

Наконец, вам определенно нужна какая-то фабрика для создания допустимого предиката, основанного на указанном типе операции. Это ваша основная точка расширения: ввод новых операторов или переопределение старых должно быть таким же простым, как изменение вашего Factory.Create(...) метода. Убедитесь, что имеется интерфейс IFactory, чтобы избежать статических зависимостей кода not-unit-testable . Внутренне это может быть так же просто, как HashTable, основанное на подходящем массиве string / char.

Надеюсь, это поможет. Дайте мне знать, если вам нужно больше примеров кода.

...