Вопрос: Что такое хороший дизайн для выполнения методов, основанных на логических значениях в базе данных на Java?


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

Ниже приведен образец шаблона

1 Rule1 true
2 Rule2 false
3 Rule3 true
4 Rule4 true

Итак, теперь мне нужно выполнить методы method1 (), method3 () и method4 () соответственно.

Один простой способ может использоваться If(rulee == true) ExecuteMethod ();

Во-вторых, с помощью коммутатора для выполнения случаев (вызовы методов)

Примечание. Возможно, нам придется выполнять методы в разных местах (методах). Поэтому, пожалуйста, не считайте, что все методы будут вызваны одним методом.

Могу ли я использовать АОП случайно?


13


источник


Ответы:


Вы можете определить базовый интерфейс как

public interface Rule {
  boolean canExecute();
  void execute();
}

и преобразовать методы в Rule интерфейса. Булево значение в базе данных будет отображаться в canExecute() возвращаемое значение.

Было бы неплохо, если бы методы стали сложными, их было несколько, и родительский класс начинает выглядеть как Объект Бога ,


12



Используйте 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);
    }

}

6



Вы можете просто вызвать все методы и выполнить часть проверки в реализации метода, например:

void rule1(Object... args){
  if (!applyRule1){
   return;
  }
...
}

При таком подходе вы можете уменьшить циклическую сложность и предотвратить такие инструменты, как PMD  от жалоб.


4



Другим подходом является сохранение имен методов в виде строк в базе данных. Если ваша база данных поддерживает массивы, это особенно просто.

Затем в 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));
  }
}

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


3



Обновить:  Я заменил 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,


3



Если я правильно понимаю проблему, она должна работать. У вас может быть такой метод, как ниже, и вызвать его из любого места.

Или эти логические значения также могут быть правилом, и вы можете добавить несколько методов в один 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);

2



Вместо хранилища 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> Вызов методов


2



Позволяет решить эту проблему с помощью подхода, управляемого базой данных, и Spring AOP.

У вас несколько сотен правил, и вы не хотите загрязнять текущий код с помощью шаблона, например 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 для методов перехвата, обращайтесь к Весенний АОП

Вот код 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 можно использовать в сочетании с интерфейсом на основе правил для перехвата методов (используя прокси), изучить имя метода, которое было перехвачено, найти активное состояние для этого метода и либо запустить метод , или прервать его.


2