【问题标题】:If clause optimizationif子句优化
【发布时间】:2017-11-14 14:38:55
【问题描述】:

我需要编写一个 IF 子句(我们使用的是 Java 8)。相关变量为groupprivilegerolelevelclass。在某些情况下需要执行action。我用以下方式编写了算法:

if (group == G1) {
  if (privilege == P1) {
    // Perform the action if a user has this particular privilege
    perform_action_and_exit();
  } else {
    if (role == R1) {
      if ((level <= 2 && class == C1) || (level == 1 && (class == C2 || class == C3))) {
        perform_action_and_exit();
      }
      // Ignore other combinations of level and class
    }
    // Ignore other roles
  }
} else {
  // Perform the action for any group other than G1
  perform_action_and_exit();
}

有没有更简洁的方式来写这个条件?

谢谢。

【问题讨论】:

  • 您可以将条件分别放在 1 个函数/方法中...考虑单一职责和代码复杂性
  • 在所有这些情况下,您似乎唯一想做的就是perform_action_and_exit() - 因此,如果您按照 B001 的建议组织方法中的条件,您将能够执行类似的操作:if (shouldExit()) { perform_action_and_exit(); }
  • 这取决于你眼中什么是更干净的方式。我认为,它现在的构建方式只能抽象事物,因此您可以将这个验证编码在一个专门的对象中,这些对象链接在一起,可以返回下一个阶段、快捷阶段或空操作。例如,GroupHandler.get(group) 如果是G1,则返回StagedGroupHandler,否则为ShortcutHandler(只会返回动作执行阶段,不会进行任何验证)。然而,这只会简单地拆分子句并将其隐藏起来,而不是消除或普遍改进它。
  • level 的允许值是多少?
  • 顺便说一下,class 不能是变量。

标签: java optimization coding-style


【解决方案1】:

无论如何,您需要将操作与测试分开:

if (shouldPerform()) {
    perform_action_and_exit();
}

有时,提前退出会使代码清晰:

boolean shouldPerform() {
    if (group != G1) {
        return true;
    }
    if (role != R1) {
        return false;
    }
    if (level > 2) {
        return false;
    }
    if (class == C1) {
        return true;
    }
    if (level != 1) {
        return false;
    }
    return class == C2 || class == C3;
}

有时,他们不会。看看这个,然后决定你是否能一眼看懂。你可能被告知类似

  • 唯一的特权组是 G1,其他组不受限制。
  • 只有 R1 可以执行特权操作...
  • ...

然后这个早期的退出完全映射到描述。

在任何情况下,我都不推荐使用复杂的布尔表达式。任何超过一两行的东西都很难理解。使用带有简单表达式的方法或变量可以使代码更具可读性。


不要在意这里的效率。 很有可能你的所有条件版本都将产生同样快的代码,因为 JVM 内联方法和积极地转换表达式。 所以不要进行不必要的微优化,这就是机器的用途。

此外,我敢打赌perform_action_and_exit 的执行时间至少比测试长四个数量级。测试可能需要几十个纳秒(悲观地假设分支预测错误),而操作可能会访问一个数据库,这意味着一些毫秒

【讨论】:

    【解决方案2】:

    您可以使用Boolean algebra 重新组织和简化您的条件:

    if (group != G1
        || privilege == P1
        || role == R1
                && (level <= 2 && class == C1
                    || level == 1 && (class == C2 || class == C3))
    ) {
        perform_action_and_exit();
    }
    

    【讨论】:

      【解决方案3】:

      首先,想想你是否真的需要违反Tell don't ask

      您的代码会获得一些状态信息,然后据此做出决定。在一个好的 OO 模型中,您只需告诉某个对象:“做正确的事”。然后你依靠多态性来确保doTheRightThing() 是适合当前状态的东西。

      如果你不想走那条路,你至少应该看看 single layer of abstraction 原则。含义:将您的条件表达式转换为小的辅助方法,以便您的代码读取:

      if (isXCondition()) { 
        doX();
      }
      if (isYCondition()) ...
      

      【讨论】:

        【解决方案4】:

        以下是示例重构,以使您的代码更清晰,如果 perform_action_and_exit() 在整个代码中都是相同的方法,则可以进一步重构

        if (group == G1) {
          doG1Stuff();
        } else {
          // Perform the action for any group other than G1
          perform_action_and_exit();
        }
        
        void doG1Stuff(){
         if (privilege == P1) {
            // Perform the action if a user has this particular privilege
            perform_action_and_exit();
          } else {
            if (shouldActionBePerformed()) {
                perform_action_and_exit();
            }
          }
        }
        
        boolean shouldActionBePerformed(){
            return role == R1 && (level <= 2 && class == C1) || (level == 1 && (class == C2 || class == C3));
        }
        

        【讨论】:

          【解决方案5】:

          声明式解决方案在业务逻辑中存在多个条件的情况下肯定更可验证和可追溯。嵌套的 if-complex 不是。声明式您拥有调用该操作的最终标准。

          但是,如果您不想使用表达式语言、java 脚本 API、JShell,则必须执行以下操作:

          class Criteria {
              Group group;
              Privilege privilege;
              Role role;
              Level level;
          }
          class GuardedAction {
              String caseName; // For tracability, an auditing name.
              Predicate<Criteria> guard;
              Runnable action;
          }
          
          List<GuardedAction> guardedActions;
          

          填写此列表,可能来自 XML。 版本号对于追溯客户在旧日志中报告的“错误”很有用。

          Criteria criteria = ...
          for (GuardedAction ga : guardedActions) {
              if (ga.guard.test(criteria)) {
                  System.out.println(ga.caseName);
                  Logger.log(Level.INFO(ga.toString()); // With a detailed toString.
                  ga.action.run();
                  break;
              }
          }
          

          带有业务逻辑的嵌套 if 很难讨论。

          【讨论】:

            【解决方案6】:

            代码看起来很乱,你已经重复了perform_action_and_exit(); 三遍。考虑单一责任。

            //all the code should be replaced by this:
            if(should_perform_action_and_exit()){
               perform_action_and_exit();
            }
            ....
            

            然后为每个操作创建方法/类(更适合目的)

            boolean should_perform_action_and_exit(){
            
               return groupCheck() || privilageCheck() || roleCheck();
            
            }
            

            现在是最后三种方法:

            boolean groupCheck(){
               return group != G1;
            }
            
            boolean privilageCheck(){
               return privilege == P1;
            }
            
            boolean roleCheck(){
               return (role == R1)  &&  ((level <= 2 && class == C1) || (level == 1 && (class == C2 || class == C3)));
            }
            

            上面的代码可能看起来更多,但以后的可读性有所提高。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-05-18
              • 2010-09-07
              • 2013-09-17
              • 2017-06-16
              • 1970-01-01
              相关资源
              最近更新 更多