改进 if-else 语句的圈复杂度代码
Improve the cyclomatic complexity code for if-else statements
我在遇到圈复杂度问题的方法中有一个 if-else 语句块。我已经尝试为此使用 switch 语句,但问题仍然存在。
if (fileName.startsWith(HwErrorsEtlConstants.MR_RAW_GRADIENT_ERRORS)) {
method1()...
} else if (fileName.startsWith(HwErrorsEtlConstants.MR_RAW_PFEI_ERRORS)) {
method2()...
} else if (fileName.startsWith(HwErrorsEtlConstants.MR_RAW_RFAMP_ERRORS)) {
method3()...
} and so on...
public static void method1(IOutputWriter writer, String fileName, InputStream fileInputStream) throws IOException {
//logic for the method
}
您可以通过 Java 8 使用谓词和函数功能接口的方法解决此问题。你必须把所有的条件都通过谓词和函数,你可以添加条件匹配时需要执行的实现。
Map<Predicate,Function<String,String>> map = new HashMap<>();
Predicate<String> p = (fileName)->fileName.startsWith(HwErrorsEtlConstants.MR_RAW_GRADIENT_ERRORS);
Function<String,String> method = (input)->{ return "output";};
map.put(p,method);
Optional<String> result =
map.entrySet()
.stream()
.filter(entry->entry.getKey().test(fileName))
.map(entry->entry.getValue().apply())
.findFirst();
或
map.entrySet()
.stream()
.filter(entry->entry.getKey().test(fileName))
.forEach(entry->entry.getValue().apply());
Edit:正如您提到的,您已经检查了可以在您的方法中抛出 和 参数的 Exception
s。由于 Runnable
没有声明它可以抛出 Exceptions
并且也不接受您必须创建自己的 FunctionalInterface
的任何参数(单击 以查看它们真的是):
public interface ThrowingRunnable {
void run(IOutputWriter writer, String fileName, InputStream fileInputStream) throws IOException;
}
那么你只需要在我之前建议的下面的代码中用 ThrowingRunnable
替换 Runnable
就可以了。
您可以创建 HwErrorsEtlConstants
到特定方法的映射(使用 java 8):
static final Map<String, Runnable> MAPPING;
static {
Map<String, Runnable> temp = new HashMap<>();
temp.put(HwErrorsEtlConstants.MR_RAW_GRADIENT_ERRORS, this::method1);
temp.put(HwErrorsEtlConstants.MR_RAW_PFEI_ERRORS, this::method2);
temp.put(HwErrorsEtlConstants.MR_RAW_RFAMP_ERRORS, this::method3);
MAPPING = Collections.unmodifiableMap(temp);
}
然后在你的方法中你可以使用 Stream
s 在 Java 8:
中也有介绍
// Optional indicates a potentially absent value, it's just a wrapper around a Runnable
Optional<Runnable> optional = MAPPING
// create a Stream from the entries
.entrySet().stream()
// keep the items that match the condition and drop every other
.filter(e -> filename.startsWith(e.getKey()))
// we had Map.Entry<String, Runnable>, but now we only need the value e.g. the Runnable
.map(Map.Entry::getValue)
// short circuit, e.g. we only want the first value that matches
.findFirst();
// checks if anything is present, this is used as the MAPPING "could" be empty
if(optional.isPresent()) {
// unpack the value and call it with arguments
optional.get().run(aWriter, someFileName, anInputStream);
} else {
// nothing matched, throw error or log etc.
}
尽管如前所述,您当前的解决方案看起来不错,但我猜您正在使用 Sonar 进行代码分析。有时声纳只是有误报,所以你也可以安全地忽略它们。
进一步阅读以帮助您理解 Java 8:
- What are the advantages of using lambda expressions in java 8?
Stream
javadoc
Optional
javadoc
cyclomatic complexity 是一个问题:确实 周期。
然后使用 if/switch 在 属性 上拆分流,这不是 object-oriented。它还可能违反关注点分离:有许多方法处理完全不同的较小方面。
如果字段数较多,行数较多,考虑提取类处理一个方面
为了降低圈复杂度,检查控制调用者的流程,相同调用的重复,实用的重复代码。然后尝试(重新)移动周期。
最好是可以将循环放在一起;并致力于 Lists/Sets/Streams.
我在遇到圈复杂度问题的方法中有一个 if-else 语句块。我已经尝试为此使用 switch 语句,但问题仍然存在。
if (fileName.startsWith(HwErrorsEtlConstants.MR_RAW_GRADIENT_ERRORS)) {
method1()...
} else if (fileName.startsWith(HwErrorsEtlConstants.MR_RAW_PFEI_ERRORS)) {
method2()...
} else if (fileName.startsWith(HwErrorsEtlConstants.MR_RAW_RFAMP_ERRORS)) {
method3()...
} and so on...
public static void method1(IOutputWriter writer, String fileName, InputStream fileInputStream) throws IOException {
//logic for the method
}
您可以通过 Java 8 使用谓词和函数功能接口的方法解决此问题。你必须把所有的条件都通过谓词和函数,你可以添加条件匹配时需要执行的实现。
Map<Predicate,Function<String,String>> map = new HashMap<>();
Predicate<String> p = (fileName)->fileName.startsWith(HwErrorsEtlConstants.MR_RAW_GRADIENT_ERRORS);
Function<String,String> method = (input)->{ return "output";};
map.put(p,method);
Optional<String> result =
map.entrySet()
.stream()
.filter(entry->entry.getKey().test(fileName))
.map(entry->entry.getValue().apply())
.findFirst();
或
map.entrySet()
.stream()
.filter(entry->entry.getKey().test(fileName))
.forEach(entry->entry.getValue().apply());
Edit:正如您提到的,您已经检查了可以在您的方法中抛出 和 参数的 Exception
s。由于 Runnable
没有声明它可以抛出 Exceptions
并且也不接受您必须创建自己的 FunctionalInterface
的任何参数(单击
public interface ThrowingRunnable {
void run(IOutputWriter writer, String fileName, InputStream fileInputStream) throws IOException;
}
那么你只需要在我之前建议的下面的代码中用 ThrowingRunnable
替换 Runnable
就可以了。
您可以创建 HwErrorsEtlConstants
到特定方法的映射(使用 java 8):
static final Map<String, Runnable> MAPPING;
static {
Map<String, Runnable> temp = new HashMap<>();
temp.put(HwErrorsEtlConstants.MR_RAW_GRADIENT_ERRORS, this::method1);
temp.put(HwErrorsEtlConstants.MR_RAW_PFEI_ERRORS, this::method2);
temp.put(HwErrorsEtlConstants.MR_RAW_RFAMP_ERRORS, this::method3);
MAPPING = Collections.unmodifiableMap(temp);
}
然后在你的方法中你可以使用 Stream
s 在 Java 8:
// Optional indicates a potentially absent value, it's just a wrapper around a Runnable
Optional<Runnable> optional = MAPPING
// create a Stream from the entries
.entrySet().stream()
// keep the items that match the condition and drop every other
.filter(e -> filename.startsWith(e.getKey()))
// we had Map.Entry<String, Runnable>, but now we only need the value e.g. the Runnable
.map(Map.Entry::getValue)
// short circuit, e.g. we only want the first value that matches
.findFirst();
// checks if anything is present, this is used as the MAPPING "could" be empty
if(optional.isPresent()) {
// unpack the value and call it with arguments
optional.get().run(aWriter, someFileName, anInputStream);
} else {
// nothing matched, throw error or log etc.
}
尽管如前所述,您当前的解决方案看起来不错,但我猜您正在使用 Sonar 进行代码分析。有时声纳只是有误报,所以你也可以安全地忽略它们。
进一步阅读以帮助您理解 Java 8:
- What are the advantages of using lambda expressions in java 8?
Stream
javadocOptional
javadoc
cyclomatic complexity 是一个问题:确实 周期。
然后使用 if/switch 在 属性 上拆分流,这不是 object-oriented。它还可能违反关注点分离:有许多方法处理完全不同的较小方面。
如果字段数较多,行数较多,考虑提取类处理一个方面
为了降低圈复杂度,检查控制调用者的流程,相同调用的重复,实用的重复代码。然后尝试(重新)移动周期。 最好是可以将循环放在一起;并致力于 Lists/Sets/Streams.