我有以下实用方法,我正在使用多个 if 语句并遇到认知复杂性问题。我浏览了一些链接,但我无法理解应该如何更改代码而不影响此方法的用户。
public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken){
String key=null;
boolean isValidWrapper = false;
if (wrapper != null && wrapper.length() > 7
&& wrapper.substring(0, 6).equalsIgnoreCase("XYZ"))
{
wrapper= wrapper.substring(7, wrapper.lastIndexOf('.')+1);
}
if(wrapper != null && wrapper.equalsIgnoreCase("TFR")) {
isValidWrapper=Boolean.TRUE;
}
try {
key = wrapper.getKey();
}
catch (Exception exception) {
return isValidWrapper;
}
if(key!=null) {
Date tokenExpiryTime = key.getExpiresAt();
if(tokenExpiryTime!=null) {
return isValidWrapper;
}
String algorithm=key.getAlgorithm();
if(!DESIRED_ALGO.equals(algorithm)) {
return isValidWrapper;
}
String value6=key.getType();
if(!DESIRED_TYPE.equals(value6)) {
return isValidWrapper;
}
if(key.getValue1()!=null && key.getValue2().size()>0 && key.getValue3()!=null && key.getValue4()!=null && key.getValue5()!=null) {
isValidWrapper=Boolean.TRUE;
}
}
return isValidWrapper;
}
请分享您重构此代码的建议。
我不认为将多个
if
条件合并为一个或简单地进行代码清理(例如通过更改某些指令的顺序)可以解决您的问题。
您的代码不符合单一责任原则。您应该将这个大方法重构为更小的部分。因此,它将是可测试的,更易于维护和阅读。我花了一些时间做了这个:
public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken) {
final WrapperClass unpackedWrapper = unpackWrapper(wrapper);
boolean wrapperValid = isUnpackedWrapperValid(unpackedWrapper);
Key key = null;
try {
key = unpackedWrapper.getKey();
} catch (final Exception exception) {
return wrapperValid;
}
if (key != null) {
if (doesKeyMeetsBasicConditions(key)) {
return wrapperValid;
}
if (doesKeyMeetsValueConditions(key)) {
return true;
}
}
return wrapperValid;
}
protected static WrapperClass unpackWrapper(final WrapperClass wrapper) {
if (wrapper != null && wrapper.length() > 7 && wrapper.substring(0, 6).equalsIgnoreCase("XYZ")) {
return wrapper.substring(7, wrapper.lastIndexOf('.') + 1);
}
return wrapper;
}
protected static boolean isUnpackedWrapperValid(final WrapperClass wrapper) {
return wrapper != null && wrapper.equalsIgnoreCase("TFR");
}
protected static boolean doesKeyMeetsBasicConditions(final Key key) {
Date tokenExpiryTime = key.getExpiresAt();
if (tokenExpiryTime != null) {
return true;
}
String algorithm = key.getAlgorithm();
if (!DESIRED_ALGO.equals(algorithm)) {
return true;
}
String value6 = key.getType();
return !DESIRED_TYPE.equals(value6);
}
protected static boolean doesKeyMeetsValueConditions(final Key key) {
return key.getValue1() != null && key.getValue2().size() > 0
&& key.getValue3() != null && key.getValue4() != null
&& key.getValue5() != null;
}
我不知道域逻辑,所以我的一些方法有愚蠢的名称等。正如你所看到的,现在你有很多较小的方法,没有很多分支(
if
条件) - 更容易测试(静态代码不太好,但是您可以使用例如 PowerMock)来模拟它。
一些重写带来了简化,但仍然可以改进。
public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken){
if (wrapper != null && wrapper.length() > 7
&& wrapper.substring(0, 6).equalsIgnoreCase("XYZ"))
{
wrapper = wrapper.substring(7, wrapper.lastIndexOf('.')+1);
}
boolean isValidWrapper = wrapper != null && wrapper.equalsIgnoreCase("TFR");
try {
String key = wrapper.getKey();
if (key != null && key.getExpiresAt() == null
&& DESIRED_ALGO.equals(key.getAlgorithm())
&& DESIRED_TYPE.equals(key.getType())
&& key.getValue1() != null && !key.getValue2().isEmpty()
&& key.getValue3() != null && key.getValue4() != null
&& key.getValue5() != null) {
isValidWrapper = true;
}
}
catch (Exception exception) {
// DO NOTHING
}
return isValidWrapper;
}
评论后:在这里我捕获所有调用的任何异常。
首先,Sonar 应该给你更多的标志:重用
wrapper
参数通常是一种不好的做法,NPE 调用 wrapper.getKey
因为 wrapper
可以为 null,但无论如何,这不是重点...
尝试通过创建局部布尔变量来减少
if
语句的数量(如果您的测试少于 5 或 6 个,但通常可读性较差,则可能是 1 个大 if
语句)。完成后,您应该只有 1 个块来测试这些布尔变量,并有一个 return 语句,如上面的示例(不一定准确!):
boolean expired = tokenExpiryTime != null;
boolean desiredAlgo = DESIRED_ALGO.equals(key.getAlgorithm());
boolean desiredType = DESIRED_TYPE.equals(value6);
if (expired || !desiredAlgo || !desiredType) {
return isValidWrapper;
}
但是,如果这种算法触发了它,你的认知复杂度似乎很低......
降低算法复杂性的另一个重要方法是将子代码块(循环、if 和 try-catch)转换为私有方法。在您的示例中,它可能类似于
checkWrapperValidity
方法,负责返回 isValidWrapper
的每个测试
请在反应中的特定文件顶部添加以下行,
/* eslint-禁用 sonarjs/认知复杂性 */
*我已经使用 eslint 开发了 React 应用程序,所以根据您的构建平台,您可以更改