2015-10-22 51 views
8

我有以下方法的圈复杂度:减少的Java方法

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

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

    if (checkMapProperty(additionalInfo, "gender")) { 
     client.setGender(additionalInfo.get("gender").toString()); 
    } 
    if (checkMapProperty(additionalInfo, "race")) { 
     client.setRace(additionalInfo.get("race").toString()); 
    } 
    if (checkMapProperty(additionalInfo, "ethnicity")) { 
     client.setEthnicity(additionalInfo.get("ethnicity").toString()); 
    } 
    ..... 

12更多,如果以类似的方式使用的语句。唯一的区别是不同的setter方法名称和不同的参数。 现在,随着同样的模式一再重复,是否有一种方法可以降低代码复杂度?

+0

您可以创建一个映射'{“race”:Client :: setRace,...}'或使用反射为字符串列表查找适当的设置器。不确定这是否会降低复杂性,但可能会减少重复。我想我会保持这种方式。更好地思考“为什么这会重复15次?”比想“这个代码在干什么?” –

+0

一个值得提问的问题:为什么您的信息在地图上?你能否在早些时候将你的信息存储在'Client'中?通常答案是“否”,你必须咬下子弹,但有时你可以避免必须从地图上填充对象。 – biziclop

+0

你的意思是“圈复杂性”还是“更少的代码行数?”对于N个东西的for循环可以将2^N添加到圈复杂度中,即使它看起来更好。对于如何改进不会改变圈复杂度的代码,你会得到很多答案。 – djechlin

回答

2

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

使用反射可以遍历值列表并在客户端对象中调用适当的方法。这将摆脱复杂性,并更干净/更强大。但是它也会执行得更慢。

基本上,虽然你有这样的情况,你一直在做几乎但不完全相同的操作,这总是很棘手。

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

相同的圈复杂度。 – djechlin

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的字段,这不会对您有所帮助。

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); 
    ..... 
+0

相同的圈复杂度。 – djechlin

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<?>>避免重复调用更加容易。

0

只作为练习回答OP的问题而言,我想创建一个Map关联与二传手接口的属性:

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:这显然是不建议用于生产代码。将集合的所有元素复制到列表以交叉两个集合 - 为了防止代码被测试/写入的条件 - 是非常昂贵的。