简体   繁体   English

在Java中的数据库中基于布尔值执行方法的好设计是什么?

[英]What is a good design to execute the methods based on boolean values in Database in Java?

We have few rules, which are Implemented as methods in Java. 我们几乎没有规则,它们在Java中作为方法实现。 But sometimes we need to bypass the rules. 但有时我们需要绕过规则。 So for each rule, we have a boolean Indicator to indicate whether to execute or not. 因此,对于每个规则,我们都有一个布尔指示符来指示是否执行。 What can be a good design to map the methods to boolean values in Database and execute methods based on the boolean values. 将方法映射到Database中的布尔值并基于布尔值执行方法可能是一个很好的设计。

Below is sample template 下面是示例模板

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

So, now I need to execute method1(), method3() and method4() respectively. 所以,现在我需要分别执行method1(),method3()和method4()。

One Simple way can be using If(rulee == true) executeMethod(); 一种简单的方法可以是使用If(rulee == true) executeMethod();

Second is using a Switch to execute the cases (method calls) 其次是使用Switch来执行案例(方法调用)

Note: We may need to execute the methods in different locations(methods). 注意:我们可能需要在不同的位置(方法)执行方法。 So please dont consider that all the methods will be called from a single method. 所以请不要考虑从单一方法调用所有方法。

Can I make use of AOP by any chance? 我有机会可以使用AOP吗?

You could define the basic interface as 您可以将基本界面定义为

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

and convert the methods into Rule interface implementations. 并将方法转换为Rule接口实现。 The boolean value in the database would map to canExecute() return value. 数据库中的布尔值将映射到canExecute()返回值。

This would be a good idea if methods are becoming complex, there's more than a few of them and the parent class is starting to look like a God Object . 如果方法变得复杂,这将是一个好主意,它们不止一些,而且父类开始看起来像神对象

Use Java 8 Stream api and Enums. 使用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);
    }

}

You can just call all methods and do the validation part within the method implementation, eg: 您可以调用所有方法并在方法实现中执行验证部分,例如:

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

With that approach, you can reduce cyclomatic complexity and prevent tools such as PMD from complaining. 通过这种方法,您可以减少圈复杂度并防止PMD等工具抱怨。

Update: I replaced Consumer interface with Runnable in my original answer, because it aligns with example in the question better. 更新:我在原始答案中用Runnable替换了Consumer接口,因为它更好地与问题中的示例对齐。

You can try to upgrade your Rule entity, here is an idea using Runnable interface: 您可以尝试升级您的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;
        }
    }
}

Example of the use: 使用示例:

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 is a method reference that would invoke the method demo.m1() , and the same for m2 . demo::m1是一个方法引用 ,它将调用方法demo.m1()m2

Another approach is to store the method names as strings in the database. 另一种方法是将方法名称存储为数据库中的字符串。 If your database supports arrays, that's particularly easy. 如果您的数据库支持数组,那就特别容易了。

Then in Java you can set up an executor that accepts a String name and execute the respective rule: 然后在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));
  }
}

An advantage of this approach is that you don't need to change the database schema to add a rule. 此方法的一个优点是您无需更改数据库架构即可添加规则。 Just start storing the new rule's string name. 只需开始存储新规则的字符串名称即可。 A disadvantage is that if you need to query on presence of or absence of a given rule, the database must support indexes over arrays. 缺点是,如果需要查询给定规则的存在或不存在,则数据库必须支持索引而不是数组。

Instead of store Boolean you can store method names in this field accordingly. 您可以相应地在此字段中存储方法名称,而不是存储布尔值。 Then all you need to do would be invoke that method using reflection. 然后你需要做的就是使用反射来调用该方法。

Table: 表:

Id RULE_NAME METHOD_NAME
1 Rule1 method1
2 Rule2 
3 Rule3 method3
4 Rule4 method4

The method can be invoked like this: 可以像这样调用该方法:

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);
    }
}

Reflection API > Invoking Methods Reflection API>调用方法

If I understand the problem correctly then it should work. 如果我正确理解了问题,那么它应该可行。 You can have a method like below and call it from anywhere. 您可以使用下面的方法并从任何地方调用它。

Or these booleans can also be a rule and you can add multiple methods in one IF condition 或者这些布尔值也可以是一个规则,您可以在一个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);

Lets solve this problem with a database driven approach, and Spring AOP. 让我们用数据库驱动的方法和Spring AOP来解决这个问题。

You have several hundred rules, and do not wish to pollute the current code with boilerplate code like void method1() { if (!rule1) return; .. do method } 你有几百个规则,并且不希望用样板代码污染当前代码,如void method1() { if (!rule1) return; .. do method } void method1() { if (!rule1) return; .. do method } or have to create additional interfaces which all rule based methods must implement. void method1() { if (!rule1) return; .. do method }或必须创建所有基于规则的方法必须实现的附加接口。

Spring AOP provides a means to leave the current base in tact, and instead have methods intercepted (via a proxy) to determine if the method should run or not. Spring AOP提供了一种方法来保留当前基础,而是通过代理拦截方法来确定方法是否应该运行。 You write the proxy code once, and the only ongoing requirement is to keep the database up to date with new rules. 您只编写一次代理代码,唯一的持续要求是使数据库保持最新的规则。

Step 1 : Build a database schema which maps method names to boolean values 步骤1 :构建将方法名称映射到布尔值的数据库模式

method_name VARCHAR(100), is_rule_active tinyint(1); method_name VARCHAR(100),is_rule_active tinyint(1);

There will be one row for each rule. 每条规则都会有一行。 The row will contain the method name (as it appears in the java code) and a boolean true=active, false=not active. 该行将包含方法名称(如java代码中所示)和布尔值true = active,false = not active。

Step 2: Build an interface to the database (DAO) 第2步:构建数据库接口(DAO)

You need a simple abstraction to the database. 您需要对数据库进行简单抽象。 Something like: 就像是:

public interface RuleSelectionInterface {
    boolean isRuleActive(String methodName);
}

The implementation will be basic DAO code, which will query for the row with method_name equal to methodName . 实现将是基本的DAO代码,它将查询method_name等于methodName的行。 For simplicity, and to demonstrate, I used a Map instead: 为简单起见,为了演示,我使用了Map:

@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);
    }

}

Step 3: Create a Spring AOP aspect 第3步:创建Spring AOP方面

An aspect is created to intercept method calls, and determine when the call should be executed. 创建一个方面来拦截方法调用,并确定何时应该执行调用。

To allow execution to be continued, or aborted, you use an @Around advice, which will be passed the execution point (by means of a ProceedingJoinPoint ) from which you can either abort (the proxy method simply returns) or run the code by using the proceed method. 为了允许执行继续或中止,您使用@Around建议,它将传递执行点(通过ProceedingJoinPoint ),您可以从中止(代理方法只返回)或使用运行代码proceed方法。

There is some choice here on which methods should be intercepted (this is done by defining pointcuts). 这里有一些选择应该拦截哪些方法(这是通过定义切入点来完成的)。 This example will intercept methods with names starting with rule : 此示例将拦截名称以rule开头的方法:

@Around("execution(* rule*(..))") 

You could intercept all methods, or methods based on naming patterns, etc. For a detailed understanding of how to create pointcuts to intercept methods refer to Spring AOP 您可以拦截所有方法或基于命名模式的方法等。有关如何创建切入点以截取方法的详细信息,请参阅Spring AOP

Here is the AOP code, which is called upon method interception, and which uses your database rule interface to look up if the rule is active for this method name: 下面是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)");
   }

}

Sample usage: 样品用法:

I created a simple class with two methods (however this approach works regardless of how many classes/methods you have rule based methods for). 我使用两种方法创建了一个简单的类(但是无论您使用基于规则的方法有多少类/方法,这种方法都可以工作)。

@Component
public class MethodsForRules {

public void rule1Method() {
    System.out.println("Rule 1 method");
}

public void rule2Method() {
    System.out.println("Rule 2 method");
}
}

You will have noticed in the Map that rule1Method is set to true, and rule2Method is set to false. 您将在Map中注意到rule1Method设置为true,rule2Method设置为false。

When the code tries to run rule1Method and rule2Method: 当代码尝试运行rule1Method和rule2Method时:

MethodsForRules r;  // Is a Spring managed bean.
r.rule1Method();
r.rule2Method();

Produces the following output: 产生以下输出:

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

Summary: 摘要:

This demonstration has shown how Spring AOP can be used, in combination with a rules based interface, to intercept methods (by using a proxy), examine the method name which was intercepted, lookup the active status for this method, and either run the method, or abort it. 此演示展示了如何结合基于规则的接口使用Spring AOP来拦截方法(通过使用代理),检查被拦截的方法名称,查找此方法的活动状态,以及运行方法,或中止它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM