降低java方法的圈复杂度

时间:2015-10-22 15:09:10

标签: java if-statement sonarqube cyclomatic-complexity

我有以下方法:

[DataContract]
public class InvestigatorGroupData
{

    [DataMember]
    public int InvestigatorGroupId { get; set; }

    [DataMember]
    public string InvestigatorGroupName { get; set; }

}
如果语句以类似的方式使用,则会再增加12个。唯一的区别是不同的setter方法名称和不同的参数。 现在,由于一次又一次地重复相同的模式,有没有办法降低代码复杂度?

6 个答案:

答案 0 :(得分:2)

不容易,也不是没有使用反射。

使用反射,您可以遍历值列表并在客户端对象中调用适当的方法。这将摆脱复杂性,更清洁/更强大。然而,它也会表现得更慢。

从根本上说,虽然你遇到的情况是你一直在做几乎但不完全相同的操作,但这总是很棘手。

答案 1 :(得分:1)

您可以使用Java 8功能接口执行此操作。它至少可以摆脱重复的条件陈述。

private void doRepetitiveThing(Map info, String key, Consumer<String> setterFunction) {
   if(checkMapProperty(info, key)) {
       setterFunction.accept(info.get(key).toString());
   }
}

private void setClientAdditionalInfo(Map map, Client client, User user) {

    Map additionalInfo = (Map) map.get("additionalInfo");

    doRepetitiveThing(additionalInfo, "gender", client::setGender);
    doRepetitiveThing(additionalInfo, "race", client::setRace);
    doRepetitiveThing(additionalInfo, "ethnicity", client::setEthnicity);
   .....

答案 2 :(得分:1)

不确定这是否真的降低了圈复杂度,但它使代码更漂亮。使用Java 8更容易。

private void setClientAdditionalInfo(Map<String, Object> map, Client client, User user) {
    Map<String, ?> additionalInfo = (Map<String, Object>) map.get("additionalInfo");
    setIfPresent(additionalInfo, "gender", client::setGender);
    setIfPresent(additionalInfo, "race", client::setRace);
    setIfPresent(additionalInfo, "ethnicity", client::setEthnicity);
}

private void <T> setIfPresent(Map<String, ?> additionalInfo,
                              String property,
                              Consumer<T> consumer) {
    if (checkMapProperty(additionalInfo, property)) {
        consumer.apply((T)additionalInfo.get(property));
    }
}

如果您愿意,可以制作Map<String, Consumer<?>>以避免重复通话。

答案 3 :(得分:0)

我使用enum使用name().toLowercase()作为密钥,将设置器与地图键连接起来。

enum Setter {

    Gender {

                @Override
                void set(Client client, Thing value) {
                    client.setGender(value);
                }

            },
    Race {

                @Override
                void set(Client client, Thing value) {
                    client.setRace(value);
                }

            },
    Ethnicity {

                @Override
                void set(Client client, Thing value) {
                    client.setEthnicity(value);
                }

            };

    void setIfPresent(Client client, Map<String, Thing> additionalInfo) {
        // Use the enum name in lowercase as the key.
        String key = this.name().toLowerCase();
        // Should this one be set?
        if (additionalInfo.containsKey(key)) {
            // Yes! Call the setter-specific set method.
            set(client, additionalInfo.get(key));
        }
    }

    // All enums must implement one of these.
    abstract void set(Client client, Thing value);
}

private void setClientAdditionalInfo(Map map, Client client, User user) {
    Map additionalInfo = (Map) map.get("additionalInfo");
    for (Setter setter : Setter.values()) {
        setter.setIfPresent(client, additionalInfo);
    }
}

答案 4 :(得分:0)

如果地图不包含属性,假设Client的字段可以设置为null,则可以创建一个类:

class MapWrapper {
   private final Map map;

   MapWrapper(Map map) {
      this.map = map;
   }

   String get(String key) {
       if (checkMapProperty(map,key)) {
          return map.get(key).toString();
       } else {
          return null;
       }
       // or more concisely:
       // return checkMapProperty(map,key) ? map.get(key).toString() : null;
   }
}

然后

private void setClientAdditionalInfo(Map map, Client client, User user) {

    Map additionalInfo = (Map) map.get("additionalInfo");
    MapWrapper wrapper = new MapWrapper(additionalInfo);

    client.setGender(wrapper.get("gender"));
    ...
}

但是,如果要求在地图中没有相应的关键字时保留Client字段,则无法帮助您。

答案 5 :(得分:0)

仅作为回答OP问题的练习,我创建了Map将属性与setter界面相关联:

private static Map<String, ClientSetter> setters = new HashMap<>();

interface ClientSetter {
    void set(Client client, Object value);
}

static {
    setters.put("gender", new ClientSetter() {
        @Override
        public void set(Client client, Object value) {
            client.setGender(value.toString());
        }
    });
    setters.put("race", new ClientSetter() {
        @Override
        public void set(Client client, Object value) {
            client.setRace(value.toString());
        }
    });
    setters.put("ethnicity", new ClientSetter() {
        @Override
        public void set(Client client, Object value) {
            client.setEthnicity(value.toString());
        }
    });
    // ... more setters
}

迭代可用的属性,并为set地图中的每个可用属性调用setters方法:

private void setClientAdditionalInfo(Map map, Client client, User user) {

    Map additionalInfo = (Map) map.get("additionalInfo");

    List<String> additionalInfoKeys = new ArrayList<>(additionalInfo.keySet());
    additionalInfoKeys.retainAll(setters.keySet());
    for (String key: additionalInfoKeys) {
        Object value = additionalInfo.get(key);
        setters.get(key).set(client, value);
    }
}

PS:显然这不是生产代码的建议。将集合的所有元素复制到用于交叉两组的列表 - 为了防止被测试/编写的代码中的条件 - 是非常昂贵的。