如何降低Java代码认知复杂度 解决Sonar超15阈值问题
代码认知复杂度超标原因及优化方案
为什么复杂度会超过15阈值
Sonar的认知复杂度不是只统计嵌套if条件,循环、条件分支、try/catch、逻辑与/或运算符都会累加分数,且结构嵌套层级越深,单次加分越高。
你认为代码里没有嵌套条件是判断失误:内层for循环、try/catch块都属于嵌套结构,叠加内部的if判断、逻辑运算符,累加后刚好到16分,超过15的阈值。
优化方案
1. 先修正明显的逻辑错误
你现在代码里isHls.set(true)写在了try/catch块外面,是无条件执行的——只要进入内层文件循环,不管有没有找到m3u8格式的HLS文件,isHls都会被设为true,完全不符合注释里“循环判断媒体是否为HLS”的设计目的,正确的写法是把这行代码移到isMediaHls判断为true的分支内,只有真的检测到HLS文件时才标记为true。
另外注意:如果某个路径对应的目录不存在,你现在直接return会终止整个方法,后续剩下的所有路径都不会被处理,如果不是预期逻辑,应该改成continue跳过当前无效路径即可。
2. 最低成本降复杂度(直接满足Sonar要求)
认知复杂度最通用的优化方式就是拆分子方法,把内层循环里单个文件的处理逻辑抽成独立的私有方法,原来嵌套在两层循环里的try/catch、if分支、逻辑运算符的复杂度都会被算到子方法里,主方法的复杂度会直接降到阈值以下。
抽离后的主方法代码示例:
private void processMedia(Integer mediaId, List<String> hiresPhysicalPaths) { final AtomicBoolean isHls = new AtomicBoolean(false); String mediaIdString = mediaId.toString(); // 固定路径提到循环外,避免重复调用 String rootPath = MediaRepositoryTools.getCurrentMediaPhysicalRootPath(App.getApplicationSession()); for (String hiresPhysicalPath : hiresPhysicalPaths) { REPORT.info("Process media version {} for media {} ", hiresPhysicalPath, mediaId); String folderName = StringUtils.substringBefore(hiresPhysicalPath, "/"); File dir = new File(rootPath, folderName); REPORT.info("Dir : {}", dir); File[] directoryListing = dir.listFiles(); if (directoryListing == null) { REPORT.warn("Dir is not really a directory"); // 根据业务需求决定是return终止整个流程,还是continue跳过当前无效路径 return; } for (File child : directoryListing) { // 单文件处理逻辑抽离到子方法 processSingleMediaChild(child, mediaIdString, hiresPhysicalPath, mediaId, isHls); } } App.getService(TransactionService.class).withTransaction(() -> this.updatePreviewGeneratedFlagForMedia(mediaId, isHls.get())); }
对应的单文件处理子方法:
private void processSingleMediaChild(File child, String mediaIdString, String hiresPhysicalPath, Integer mediaId, AtomicBoolean isHls) { String ext = FilenameUtils.getExtension(child.getName()).toLowerCase(); String childFileName = FilenameUtils.getBaseName(child.getName()); try { if (isMediaHls(mediaIdString, childFileName, ext)) { miseAJourFichierHLS(child); isHls.set(true); REPORT.info("Le fichier m3u8 a été mis à jour pour la version : {} du média {} ", hiresPhysicalPath, mediaId); } else { REPORT.info("Le fichier m3u8 n'a pas été mis à jour pour la version : {} du média {} ", hiresPhysicalPath, mediaId); } } catch (IOException e) { REPORT.error("Unable to update the HLS file for media " + mediaId, e); } if (childFileName.startsWith(mediaIdString) && isFileNeededToBeDeleted(child.getName(), isHls.get())) { this.copyOrDelete(child); } }
额外优化点
这个方法里没有多线程并发修改isHls的场景,完全没必要用AtomicBoolean,换成普通可变布尔值即可,不过这个不影响复杂度,属于代码规范层面的优化。
内容的提问来源于stack exchange,提问作者JhinKazama
相关产品推荐
相关产品推荐

