Java 降低圈复杂度
我有一个类,它包含20多个相同类型的字段,这些字段在对象生命周期的不同阶段填充 其中一个类方法应基于字段名返回字段值 到目前为止,我有这样的想法:Java 降低圈复杂度,java,cyclomatic-complexity,Java,Cyclomatic Complexity,我有一个类,它包含20多个相同类型的字段,这些字段在对象生命周期的不同阶段填充 其中一个类方法应基于字段名返回字段值 到目前为止,我有这样的想法: public String getFieldValue(String fieldName){ switch (fieldName.toLowerCase(){ case "id": return getId(); case "name": return getName(); ..... public String getFieldValue(Str
public String getFieldValue(String fieldName){
switch (fieldName.toLowerCase(){
case "id": return getId();
case "name": return getName();
.....
public String getFieldValue(String fieldName){
return PropertyUtils.getSimpleProperty(fieldName.toLowerCase());
}
问题是圈复杂度很高
解决这个问题最简单的方法是什么?理论上,您可以通过以下方式降低
getFieldValue()
方法的复杂性:
- 将getter方法引用存储为
中的Producer
?编辑:感谢@Filippo Possenti的评论 您可以使用Map
映射来代替开关 这里有一个例子
static interface C { String getA(); String getB(); String getC(); } @FunctionalInterface static interface FieldGetter { String get(C c); } static Map<String, FieldGetter> fields = Map.of( "a", C::getA, "b", C::getB, "c", C::getC ); static String getField(C object, String fieldNameToRetrieve) { var getter = fields.get(fieldNameToRetrieve); if(getter == null) { throw new IllegalArgumentException("unknown field"); } return getter.get(object); }
静态接口C{ 字符串getA(); 字符串getB(); 字符串getC(); } @功能接口 静态接口FieldGetter{ 字符串get(C); } 静态映射字段=Map.of( “a”,C::getA, “b”,C::getB, “c”,c::getC ); 静态字符串getField(C对象,字符串字段名检索){ var getter=fields.get(fieldnametoretrive); if(getter==null){ 抛出新的IllegalArgumentException(“未知字段”); } 返回getter.get(对象); }
为什么不为此使用反射或现有库?(或者为什么会有这种方法)假设
可能的值与bean上的getter匹配,您可以使用Apache的BeanUtils: 基本上,你可以这样做:fieldName
public String getFieldValue(String fieldName){ switch (fieldName.toLowerCase(){ case "id": return getId(); case "name": return getName(); .....
这更多的是关于提高代码可读性,而不是改进圈复杂度,所以如果您追求的是纯性能,那么这可能不是您的解决方案 如果您追求的是纯性能,那么您可以尝试利用lambdas和Mappublic String getFieldValue(String fieldName){ return PropertyUtils.getSimpleProperty(fieldName.toLowerCase()); }
import java.util.Map; import java.util.HashMap; import java.util.function.Function; public class HelloWorld{ public static class MyClass { private static Map<String, Function<MyClass, Object>> descriptor; static { descriptor = new HashMap<>(); descriptor.put("id", MyClass::getId); descriptor.put("name", MyClass::getName); } private String id; private String name; public String getId() { return id; } public String getName() { return name; } public void setId(String value) { id = value; } public void setName(String value) { name = value; } public Object getFieldValue(String fieldName) { Function fn = descriptor.get(fieldName); return fn.apply(this); } } public static void main(String []args){ MyClass mc = new MyClass(); mc.setId("hello"); mc.setName("world"); System.out.println(mc.getFieldValue("id") + " " + mc.getFieldValue("name")); } }
注意,在上面的示例中,圈复杂度仍然存在,但它在类的静态初始化器中移动了。这意味着您将在应用程序启动期间受到适度的惩罚,但在随后调用import java.util.Map; 导入java.util.HashMap; 导入java.util.function.function; 公共类HelloWorld{ 公共静态类MyClass{ 私有静态映射描述符; 静止的{ descriptor=newhashmap(); descriptor.put(“id”,MyClass::getId); descriptor.put(“name”,MyClass::getName); } 私有字符串id; 私有字符串名称; 公共字符串getId(){ 返回id; } 公共字符串getName(){ 返回名称; } 公共void setId(字符串值){ id=值; } 公共void集合名(字符串值){ 名称=值; } 公共对象getFieldValue(字符串fieldName){ 函数fn=descriptor.get(fieldName); 返回fn.应用(本); } } 公共静态void main(字符串[]args){ MyClass mc=新的MyClass(); mc.setId(“你好”); mc.setName(“世界”); System.out.println(mc.getFieldValue(“id”)+“”+mc.getFieldValue(“名称”); } }
时,您将享受更高的性能。getFieldValue
此外,如果您追求的是性能,那么您可能希望消除对小写字母的需求。。。在我的示例中,我删除了它。您可以使用枚举来代替开关或使用
映射
enum FieldExtractor implements Function<YourClass, String> { ID(YourClass::getId), NAME(YourClass::getName); // and so on private final Function<YourClass, String> delegate; FieldExtractor(Function<YourClass, String> delegate) { this.delegate = delegate; } @Override public String apply(YourClass extractFrom) { return delegate.apply(extractFrom); } static FieldExtractor fromString(String name) { return Stream.of(FieldExtractor.values()) .filter(fe -> fe.name().equalsIgnoreCase(name)) .findFirst() .orElseThrow(IllegalArgumentException::new); } }
在您的客户代码中。您可以使用单一退货。但这一变化真的会让事情变得更清晰/可读/可维护吗?有时,这些标志可以被忽略。这是不应该做的事情。但如果你真的必须这样做,这将是反射的用例。我可以使用一个返回,但这不会影响圈复杂度分数。关于反射,如果没有其他方法,我将求助于反射,但如果有不同的路径,我宁愿尝试不同的路径。每个返回的不是增加圈复杂度吗?您可以将实际字段值存储在
映射中,然后通过
例如,为什么要遍历映射?他不能直接调用公共字符串getId()实现getter{return fields.get(“id”);}
?@FilipPossenti非常感谢。默认情况下,我倾向于使用fields.get(fieldName)
,即使它根本不需要!您应该能够在这里编写方法引用而不是lambda:Stream
等等。C::getA