Что такое хороший дизайн для выполнения методов, основанных на логических значениях в базе данных на Java?
У нас мало правил, которые реализованы как методы на Java. Но иногда нам нужно обходить правила. Поэтому для каждого правила у нас есть логический индикатор, указывающий, выполнять или нет. Что может быть хорошим дизайном для сопоставления методов с булевыми значениями в базе данных и выполнения методов, основанных на логических значениях.
Ниже приведен образец шаблона
1 Rule1 true
2 Rule2 false
3 Rule3 true
4 Rule4 true
Итак, теперь мне нужно выполнить методы method1(), method3() и method4() соответственно.
Один простой способ может использовать If(rulee == true)
executeMethod();
Во-вторых, с помощью коммутатора для выполнения случаев (вызовы методов)
Примечание. Возможно, нам придется выполнять методы в разных местах (методах). Поэтому, пожалуйста, не считайте, что все методы будут вызваны одним методом.
Могу ли я использовать АОП случайно?
Ответы
Ответ 1
Вы можете определить базовый интерфейс как
public interface Rule {
boolean canExecute();
void execute();
}
и преобразовать методы в реализации интерфейса Rule
. Логическое значение в базе данных будет отображать значение canExecute()
return.
Было бы неплохо, если бы методы стали сложными, их было несколько, а родительский класс стал похож на объект Бога.
Ответ 2
Используйте Java 8 Stream api и Enums.
public class Main {
public enum Rule {
RULE1 {
@Override
public void doWork() {
}
},
RULE2 {
@Override
public void doWork() {
}
};
public abstract void doWork();
}
public static void main(String[] args) {
List<String> rules = new ArrayList<>();
rules.stream()
.map(Rule::valueOf)
.forEach(Rule::doWork);
}
}
Ответ 3
Вы можете просто вызвать все методы и выполнить часть проверки в реализации метода, например:
void rule1(Object... args){
if (!applyRule1){
return;
}
...
}
Благодаря такому подходу вы можете уменьшить циклическую сложность и не дать инструментам, таким как PMD, жаловаться.
Ответ 4
Обновление: Я заменил Consumer
интерфейс Runnable
в моем оригинальный ответ, потому что он совпадет с примером в этом вопросе лучше.
Вы можете попробовать обновить свой объект Rule
, вот идея с использованием интерфейса Runnable
:
class Rule {
private boolean isActive;
private Runnable runnable;
public Rule(boolean isActive, Runnable runnable) {
this.isActive = isActive;
this.runnable = runnable;
}
public void executeIfActive() {
if (isActive) {
runnable.run();
isActive = false;
}
}
}
Пример использования:
public class Demo {
public static void main(String[] args) {
Demo demo = new Demo();
List<Rule> rules = List.of(new Rule(true, demo::m1), new Rule(false, demo::m2));
rules.forEach(Rule::executeIfActive);
}
void m1() { ... }
void m2() { ... }
}
demo::m1
- это ссылка на метод, которая будет ссылаться на метод demo.m1()
, и тот же для m2
.
Ответ 5
Другим подходом является сохранение имен методов в виде строк в базе данных. Если ваша база данных поддерживает массивы, это особенно просто.
Затем в Java вы можете настроить исполнителя, который принимает имя String
и выполнить соответствующее правило:
import java.util.List;
import static java.util.Arrays.asList;
public class ByNameExecutor {
enum Rule {
Rule1 { @Override void rule() { System.out.println("Executed rule 1"); } },
Rule2 { @Override void rule() { System.out.println("Executed rule 2"); } },
Rule3 { @Override void rule() { System.out.println("Executed rule 3"); } },
Rule4 { @Override void rule() { System.out.println("Executed rule 4"); } },
;
abstract void rule();
}
public void execute(String ruleName) {
Rule.valueOf(ruleName).rule();
}
public void execute(List<String> ruleNames) {
ruleNames.stream().forEach(this::execute);
}
public static void main(String [] args) {
String [] methodList = { "Rule1", "Rule2", "Rule4" };
new ByNameExecutor().execute(asList(methodList));
}
}
Преимущество такого подхода заключается в том, что вам не нужно менять схему базы данных, чтобы добавить правило. Просто начните хранить новое имя строки правила. Недостатком является то, что если вам нужно запросить наличие или отсутствие данного правила, база данных должна поддерживать индексы над массивами.
Ответ 6
Вместо хранилища Boolean вы можете сохранить имена методов в этом поле соответственно. Тогда все, что вам нужно сделать, будет вызывать этот метод с использованием отражения.
Таблица:
Id RULE_NAME METHOD_NAME
1 Rule1 method1
2 Rule2
3 Rule3 method3
4 Rule4 method4
Этот метод можно вызвать следующим образом:
ResultSet srs = stmt.executeQuery("SELECT METHOD_NAME from table");
while (srs.next()) {
String methodName = srs.getString("METHOD_NAME");
if (!TextUtils.isEmpty(methodName)) {
Class<?> c = Class.forName("class name");
Method method = c.getDeclaredMethod(methodName, parameterTypes); // method name will be fetched from Database
method.invoke(objectToInvokeOn, params);
}
}
API Reflection> Вызов методов
Ответ 7
Если я правильно понимаю проблему, она должна работать. У вас может быть такой метод, как ниже, и вызвать его из любого места.
Или эти логические значения также могут быть правилом, и вы можете добавить несколько методов в одно условие IF
void executeMethods(boolean m1, boolean m2, boolean m3, boolean m4){
if(m1) m1();
if(m2) m2();
if(m3) m3();
if(m4) m4();
}
executeMethods(true,false,false,true);
Ответ 8
Позволяет решить эту проблему с помощью подхода, управляемого базой данных, и Spring AOP.
У вас есть несколько сотен правил и не хотите загрязнять текущий код с помощью шаблона кода, например void method1() { if (!rule1) return;.. do method }
void method1() { if (!rule1) return;.. do method }
или создать дополнительные интерфейсы, которые должны реализовывать все основанные на правилах методы.
Spring AOP обеспечивает возможность оставить текущую базу в такте и вместо этого перехватывать методы (через прокси), чтобы определить, должен ли этот метод работать или нет. Вы пишете прокси-код один раз, и единственным постоянным требованием является обновление базы данных новыми правилами.
Шаг 1. Создайте схему базы данных, которая отображает имена методов в логические значения
method_name VARCHAR (100), is_rule_active tinyint (1);
Для каждого правила будет одна строка. Строка будет содержать имя метода (как показано в java-коде) и логическое значение true = active, false = not active.
Шаг 2. Создание интерфейса к базе данных (DAO)
Вам нужна простая абстракция для базы данных. Что-то вроде:
public interface RuleSelectionInterface {
boolean isRuleActive(String methodName);
}
Реализация будет базовым кодом DAO, который будет запрашивать строку с method_name
равным methodName
. Для простоты и демонстрации я вместо этого использовал карту:
@Repository
public class RuleSelectionImpl implements RuleSelectionInterface {
Map<String, Boolean> rules;
public RuleSelectionImpl() {
rules = new HashMap<>();
rules.put("rule1Method", true);
rules.put("rule2Method", false);
}
@Override
public boolean isRuleActive(String methodName) {
if (!rules.containsKey(methodName))
return false;
return rules.get(methodName);
}
}
Шаг 3: Создайте аспект AOP Spring
Аспект создан для перехвата вызовов метода и определения того, когда должен выполняться вызов.
Чтобы разрешить выполнение или прервать выполнение, вы используете совет @Around
, которому будет передана точка выполнения (с помощью ProceedingJoinPoint
), из которой вы можете либо прервать (прокси-метод просто возвращает), либо запустить код, используя метод proceed
.
Здесь есть какой-то выбор, по которому следует перехватывать методы (это делается путем определения pointcut). В этом примере будут перехватываться методы с именами, начинающимися с rule
:
@Around("execution(* rule*(..))")
Вы можете перехватить все методы или методы, основанные на шаблонах именования и т.д. Подробное понимание того, как создавать pointcuts для методов перехвата, относится к Spring AOP
Вот код AOP, который вызван перехватом метода и который использует интерфейс базы данных для поиска, если правило для этого имени метода:
@Aspect
@Component
public class RuleAspects {
@Autowired
private RuleSelectionInterface rulesSelectionService;
@Around("execution(* rule*(..))")
public void ruleChooser(ProceedingJoinPoint jp) throws Throwable
{
Signature sig = jp.getSignature();
System.out.println("Join point signature = "+sig);
String methodName = sig.getName();
if (rulesSelectionService.isRuleActive(methodName))
jp.proceed();
else
System.out.println("Method was aborted (rule is false)");
}
}
Использование образца:
Я создал простой класс с двумя методами (однако этот подход работает независимо от того, сколько классов/методов у вас есть методы, основанные на правилах).
@Component
public class MethodsForRules {
public void rule1Method() {
System.out.println("Rule 1 method");
}
public void rule2Method() {
System.out.println("Rule 2 method");
}
}
Вы заметили бы на Карте, что для правила1Method установлено значение true, а для rule2Method установлено значение false.
Когда код пытается запустить rule1Method и rule2Method:
MethodsForRules r; // Is a Spring managed bean.
r.rule1Method();
r.rule2Method();
Производит следующий вывод:
Join point signature = void com.stackoverflow.aoparound.demo.MethodsForRules.rule1Method()
Rule 1 method <- Here is the method running
Join point signature = void
com.stackoverflow.aoparound.demo.MethodsForRules.rule2Method()
Method was aborted (rule is false) <- Here the method is aborted
Резюме:
Эта демонстрация показала, как Spring AOP можно использовать в сочетании с интерфейсом на основе правил для перехвата методов (используя прокси), изучить имя метода, которое было перехвачено, найти активное состояние для этого метода и либо запустить метод, или прервать его.