【问题标题】:What is a good design to execute the methods based on boolean values in Database in Java?在 Java 中的数据库中执行基于布尔值的方法的好的设计是什么?
【发布时间】:2018-04-13 18:21:07
【问题描述】:

我们的规则很少,在 Java 中作为方法实现。但有时我们需要绕过规则。所以对于每条规则,我们都有一个布尔指标来指示是否执行。将方法映射到数据库中的布尔值并根据布尔值执行方法是一个好的设计。

下面是示例模板

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

所以,现在我需要分别执行method1()、method3()和method4()。

一种简单的方法是使用If(rulee == true) executeMethod();

其次是使用 Switch 执行案例(方法调用)

注意:我们可能需要在不同的位置(方法)执行方法。所以请不要认为所有的方法都会从一个方法中调用。

我可以使用 AOP 吗?

【问题讨论】:

  • 什么时候需要查阅数据库?在启动时,还是一直动态?
  • 你也可以建议。截至目前,我们正在动态进行
  • 您已经在谈论规则,每个规则都有条件,那么您为什么不使用规则引擎呢?
  • 这是一个简单的条件。您是在谈论使用现有的规则引擎还是创建我自己的规则引擎?

标签: java if-statement design-patterns switch-statement spring-aop


【解决方案1】:

您可以将基本接口定义为

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

并将方法转换为Rule 接口实现。数据库中的布尔值将映射到canExecute() 返回值。

如果方法变得越来越复杂,这将是一个好主意,其中有很多方法并且父类开始看起来像God Object

【讨论】:

  • 我真的很喜欢这个想法,但我有太多的实现(超过 200 条规则)。
【解决方案2】:

使用 Java 8 Stream api 和枚举。

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】:

    另一种方法是将方法名称作为字符串存储在数据库中。如果你的数据库支持数组,那就特别简单。

    然后在 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));
      }
    }
    

    这种方法的一个优点是您无需更改数据库架构即可添加规则。只需开始存储新规则的字符串名称。一个缺点是,如果您需要查询是否存在给定规则,数据库必须支持数组索引。

    【讨论】:

      【解决方案5】:

      更新:我在原始答案中将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 是一个 method reference,它将调用方法 demo.m1()m2 也是如此。

      【讨论】:

        【解决方案6】:

        如果我正确理解了问题,那么它应该可以工作。你可以有一个像下面这样的方法并从任何地方调用它。

        或者这些布尔值也可以是一个规则,你可以在一个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);
        

        【讨论】:

          【解决方案7】:

          您可以在此字段中相应地存储方法名称,而不是存储布尔值。那么您需要做的就是使用反射调用该方法。

          表:

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

          Reflection API > Invoking Methods

          【讨论】:

          • 感谢您的回答。当我在多个位置执行方法时,这有什么帮助。此外,反射会带走可读性,并且控制会以这种方式松动。以及这种方法如何帮助仅执行那些规则(方法),因为没有与之关联的布尔值。上面的方法迫使我执行所有方法,如果我必须用布尔值控制,同样的 If 场景也会出现。
          • 类> c = Class.forName("类名"); -> 如果你想在多个位置执行方法,你可以在这里定义相应的类名,你甚至可以将方法位置存储在数据库中。关于真假问题,您可以检查method_name列的字符串长度,如果方法名称长度不为零,您可以认为它是真的,如果它是空的,那么它是假的。
          • 所以再次属于 IF 方法。
          • 抱歉,如果没有“if (!TextUtils.isEmpty(methodName))”语句,我找不到解决方案,使用反射会使您的代码清晰,您可以指定将在数据库中使用的方法。我认为值得一提。祝你好运!
          【解决方案8】:

          让我们用数据库驱动的方法和 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 的行。为简单起见和演示,我使用了 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);
              }
          
          }
          

          第 3 步:创建 Spring AOP 方面

          创建切面来拦截方法调用,并确定何时执行调用。

          要允许继续执行或中止执行,请使用@Around 通知,该通知将传递到执行点(通过ProceedingJoinPoint),您可以从中中止(代理方法简单地返回)或使用proceed 方法运行代码。

          这里有一些选择应该拦截哪些方法(这是通过定义切入点来完成的)。此示例将拦截名称以rule 开头的方法:

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

          你可以拦截所有的方法,或者基于命名模式的方法等。关于如何创建切入点来拦截方法的详细了解请参考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");
          }
          }
          

          您会注意到在 Map 中 rule1Method 设置为 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 结合基于规则的接口来拦截方法(通过使用代理),检查被拦截的方法名称,查找该方法的活动状态,以及运行该方法,或中止它。

          【讨论】:

          • 我要赏金,因为您的回答几乎涵盖了我所有的想法。我会尝试这个实施,如果我成功了会让你知道。谢谢。
          • 祝奥姆卡好运。如果您有任何问题,请告诉我。
          猜你喜欢
          • 2021-04-11
          • 1970-01-01
          • 2015-07-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-01
          • 2020-05-12
          • 1970-01-01
          相关资源
          最近更新 更多