【问题标题】:Reducing the cyclomatic complexity, multiple if statements降低圈复杂度,多个 if 语句
【发布时间】:2015-10-01 10:03:23
【问题描述】:

我有以下代码:

private Facility updateFacility(Facility newFacility, Facility oldFacility) {
    if (newFacility.getCity() != null)
        oldFacility.setCity(newFacility.getCity());
    if (newFacility.getContactEmail() != null) 
        oldFacility.setContactEmail(newFacility.getContactEmail());
    if (newFacility.getContactFax() != null) 
        oldFacility.setContactFax(newFacility.getContactFax());
    if (newFacility.getContactName() != null) 
        oldFacility.setContactName(newFacility.getContactName());
    // ......
}

大约有 14 项此类检查和分配。即除少数外,我需要修改 oldFacility 对象的所有字段。我得到了这个代码 14 的圈复杂度,根据 SonarQube,它“大于 10 个授权”。关于如何降低圈复杂度的任何想法?

【问题讨论】:

  • 就我个人而言,我认为目标不应该是降低圈复杂度——我认为应该是创建可读的代码。这是非常可读的。
  • 我猜你想要做的是选择性克隆。

标签: java sonarqube cyclomatic-complexity


【解决方案1】:

在您的程序中的某个时刻,您必须实现逻辑:

  • 如果新设施定义了属性,则相应地更新旧设施
  • 如果不是,请不要覆盖旧设施的先前值。

如果没有全局查看您的项目,您可以做的是将该逻辑移动到每个属性的设置器中:

public class Facility {

    public void setSomething(String something) {
        if (something != null) {
            this.something = something;
        }
    }

}

这样,您的update 方法将是:

private Facility updateFacility(Facility newFacility, Facility oldFacility) {
    oldFacility.setSomething(newFacility.getSomething());
    // etc for the rest
}

【讨论】:

  • 如果我们没有正确使用 setter 方法,对象仍然会为 null。
  • @Nayeem 你什么意思?你可以写一个更复杂的表达式,比如something != null && !something.isEmpty(),而不是something != null。但想法是一样的。
【解决方案2】:

我认为你可以应用 Builder Pattern 来解决这个问题,它可以帮助你消除 if 语句循环中的挫败感。更多详情请查看this link

【讨论】:

  • 构建器模式更适用于对象有很多可能的初始状态可以初始化的情况,通常会产生很多构造函数。
【解决方案3】:

您可以覆盖 Facility 类中的 hashCode 和 equals 方法,并执行以下操作:

if(!newFacility.equals(oldFacility))
 {
    //only when something is changed in newFacility, this condition will be excecuted
    oldFacility = newFacility;
 }
 return oldFacility;
 //This is just and example, you can return newFacility directly

注意 :您可以包含所有参数或仅包含决定唯一性的参数。由您决定。
希望这会有所帮助!

【讨论】:

    【解决方案4】:

    您可以将您不想修改的oldFacility 对象的字段复制到其他一些变量,然后更新整个oldFacility 对象,然后将您不想更改的字段替换为存储在其他变量中的内容。即

    private Facility updateFacility(Facility newFacility, Facility oldFacility){
        String contentNotToBeModified; // or whatever variable type
        contentNotToBeModified = oldFacility.getCity();
        // Do the same for all data that you want to keep
    
        oldFacility = newFacility;
        newFacility.setCity(contentNotToBeModified);
    }
    

    所以先复制oldFacility 中要保留的数据,然后将oldFacility 替换为newFacility,并将newFacility 的所需属性替换为oldFacility 中的数据。

    【讨论】:

      【解决方案5】:

      not null 检查对我来说似乎毫无意义,因为如果您像这样稍微修改示例,则不会抛出 NullPointerException

      private Facility updateFacility(Facility newFacility, Facility oldFacility) {
      
      if (newFacility != null) {
      
          oldFacility.setCity(newFacility.getCity());
          oldFacility.setContactEmail(newFacility.getContactEmail());
          oldFacility.setContactFax(newFacility.getContactFax());
          oldFacility.setContactName(newFacility.getContactName());
          ...
      }
      

      这将为无论如何引用空值的引用分配空值,并且不会导致任何问题。

      假设您正在执行类似newFacility.getCity().toString() 的操作,那么检查将很有用。

      【讨论】:

      • 不,OP 正在检查 newFacility 的各个属性是否为空,而不是对象本身的可空性。有一个毯子 if 将用来自 newFacility 的潜在 null 值替换 oldFacility 的非 null 属性,这不是 OP 想要的。
      【解决方案6】:

      您可以使用 Java 反射来避免复制/粘贴/写入相同的问题:

      public Facility updateFacility(Facility newFacility, Facility oldFacility)
      {
          String[] properties = {"City", "ContactEmail", "ContactFax", "ContactName"};
          for(String prop : properties) {
              try {
                  Method getter = Facility.class.getMethod("get"+prop);
                  Method setter = Facility.class.getMethod("set"+prop, getter.getReturnType());
                  Object newValue = getter.invoke(newFacility);
                  if (newValue != null)
                      setter.invoke(oldFacility, newValue);
              } catch (NoSuchMethodException | 
                      SecurityException | 
                      IllegalAccessException | 
                      InvocationTargetException ex) {
                   throw new RuntimeException(ex);
              }
          }
          ...
      }
      

      现在,当您想要以这种方式更新 Facility 类中的新属性时,您可以简单地更改 properties[] 数组。

      编辑:如果使用getter方法的返回类型来查找setter方法,则不必假设Facility的属性都是同一类型。

      注意事项:重命名方法时要小心!如果您重命名或删除 Facility 类中的方法,此代码将导致运行时错误。如果您必须更改 Facility 类的代码,则应考虑使用注释来指示应更新哪些属性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-14
        • 2020-05-07
        • 1970-01-01
        • 2020-10-03
        相关资源
        最近更新 更多