ВТФ? Может ли Spring EL также быть механизмом правил?
Вы правильно прочитали, Spring EL можно использовать не только как механизм правил, но я также использую его в широком диапазоне производственных сред.
Зачем использовать Spring EL в качестве движка правил?
По сравнению с другими механизмами правил, Spring EL более легкий и требует меньших затрат на обучение.Производительность функционального программирования может превзойти ваше воображение!
Мое приложение в продакшене
задний план
Я построил финансовую систему раньше, и финансовая система должна рассчитываться с поставщиками.Поскольку мы находимся в слабом положении в сотрудничестве, нам также необходимо удовлетворить многие необоснованные требования поставщиков.Кроме того, количество поставщиков очень велико. Кроме того, правила расчетов меняются очень быстро, поэтому дизайн должен быть очень гибким.Если используется традиционный метод жесткого кодирования, логика кода может быть чрезвычайно сложной, и ее нужно менять очень часто.
Чтобы решить эту проблему, я намереваюсь ввести скриптовый движок, чтобы упростить разработку и уменьшить сложность системы.После изучения я, наконец, решил использовать Spring EL.
Как сделать
Во-первых, давайте определим базовый объект для расчета
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
private String userId;
private Integer age;
//是否是新客
private Boolean isNew;
private LocalDate orderDate;
private BigDecimal price;
}
Если есть поставщик с правилом расчетов, которое должно разделить 80% суммы заказа, мы можем рассчитать это следующим образом.
public static void main(String[] args){
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("price * 0.8");
Order order = new Order();
order.setPrice(BigDecimal.TEN);
BigDecimal value = expression.getValue(order, BigDecimal.class);
System.out.println(value);
}
Таким образом, можно рассчитать, что необходимо рассчитать 8 юаней.Конечно, этот пример слишком простой.Давайте определим несколько сложных правил расчета и посмотрим, как это сделать.
Правило 1: новые клиенты рассчитываются по понедельникам и пятницам, а сумма расчетов равна цене * 0,2.
Правило 2: Для расчетов с возрастом более 18 лет и суммой более 10 сумма расчетов составляет (цена - 10) * 0,8
Давайте посмотрим на приведенные выше правила, которые фактически разделены на два шага: первый шаг — отсеивание несостоявшихся ордеров, а второй — собственно подсчет суммы, поэтому код тоже можно разбить на эти два шага.
Сначала я определяю несколько тестовых случаев
List<Order> orders = new ArrayList<Order>(){{
//年龄19,不是新客,周一下单,金额11
add(new Order("张三",19,false,LocalDate.of(2020,11,9),new BigDecimal(11)));
//年龄17,是新客,周五下单,金额19
add(new Order("李四",17,true,LocalDate.of(2020,11,13),new BigDecimal(19)));
//年龄17,不是新客,周六下单,金额9
add(new Order("王五",17,true,LocalDate.of(2020,11,14),new BigDecimal(9)));
}};
Основная логика заключается в следующем, сначала отфильтровали ненужные заказы, а затем урегулируйте оставшиеся приказы
public static void settle(List<Order> orders, List<String> filterRule,
String settleRule, Map<String, Expression> expressionCache) {
Stream<Order> stream = orders.stream();
for (String rule : filterRule) {
Expression expression = FunctionUtil
.cacheFunction(s -> expressionParser.parseExpression(s), rule, expressionCache);
stream = filter(stream, expression);
}
Expression expression = FunctionUtil
.cacheFunction(s -> expressionParser.parseExpression(s), settleRule, expressionCache);
stream.forEach(o -> System.out.println(o.getUserId() + expression.getValue(o)));
}
public static <T> Stream<T> filter(Stream<T> stream, Expression expression) {
return stream.filter(s -> expression.getValue(s, Boolean.class));
}
Функция FunctionUtil.cacheFunction() заключается в кэшировании выражения, поскольку стоимость создания выражения относительно высока, поэтому используйте правило String в качестве ключа и выражение в качестве значения для кэширования,Читать о функциональном программировании
выполнить код
public static void main(String[] args) {
Map<String, Expression> expressionCache = new HashMap<String, Expression>();
System.out.println("结算rule1");
List<String> filterRule1 =
Arrays.asList("orderDate.getDayOfWeek().getValue() == 1 || orderDate.getDayOfWeek().getValue() == 5", "isNew");
String settleRule1 = "price * 0.2";
settle(orders, filterRule1, settleRule1, expressionCache);
System.out.println("结算rule2");
List<String> filterRule2 = Arrays.asList("age > 18", "price > 10");
String settleRule2 = "(price - 10) * 0.8";
settle(orders, filterRule2, settleRule2, expressionCache);
}
Результаты
结算rule1
李四3.8
结算rule2
张三0.8
Как видите, с помощью Spring EL и функционального программирования нам нужно только написать правила для реализации сложной логики расчетов.
Не легко быть оригинальным.Если будете перепечатывать, укажите источник.Если считаете, что это вам поможет, ставьте лайк, спасибо!