共计 2480 个字符,预计需要花费 7 分钟才能阅读完成。
问题场景
- 接手过别人写的代码吗?修改一个变量可能引发三个隐藏 bug,这就是典型的『面条式代码』后遗症。
- 当新增需求需要翻遍 20 个类才能找到切入点时,团队开发效率会呈指数级下降。
- 生产环境出现 NullPointerException 却找不到调用链?缺乏规范化的异常处理让故障排查变成大海捞针。
优化方案(含对比代码)
1. 语义化命名与 Enum 实战
反面案例:
// 难以理解的魔法数字和缩写
public void process(int t, String n) {if (t == 1) {System.out.println(n + "_A");
} else if (t == 2) {System.out.println(n + "_B");
}
}

优化方案:
// 使用 Enum 限定类型范围,参数名自解释
public enum ProcessType {
EXPORT_PDF, // PDF 导出
GENERATE_REPORT // 报告生成
}
public void processDocument(ProcessType processType, String fileName) {switch (processType) {case EXPORT_PDF -> System.out.println(fileName + ".pdf");
case GENERATE_REPORT -> System.out.println(fileName + "_report.xlsx");
}
}
2. 单一职责方法设计
反面案例:
// 一个方法完成用户创建、校验、日志记录所有操作
public void createUser(String username, String password) {
// 校验逻辑(50 行代码)// 密码加密(30 行代码)// 数据库操作(40 行代码)// 发送欢迎邮件(20 行代码)}
优化方案:
// 每个方法只做一件事(圈复杂度 <5)public User createUser(UserDto userDto) {validateUser(userDto);
User user = convertToEntity(userDto);
saveUser(user);
sendWelcomeEmail(user);
return user;
}
private void validateUser(UserDto dto) {/* 仅参数校验 */}
private User convertToEntity(UserDto dto) {/* 仅对象转换 */}
// 其他方法同理...
3. 防御性异常处理
反面案例:
// 直接吞噬异常导致问题被掩盖
try {FileUtils.readFile(new File("config.txt"));
} catch (Exception e) {e.printStackTrace(); // 仅打印不处理
}
优化方案:
// 契约式编程:明确声明可能抛出的异常
public AppConfig loadConfig() throws ConfigNotFoundException, ConfigParseException {
try {String content = FileUtils.readFile(configPath);
return parseConfig(content);
} catch (FileNotFoundException e) {throw new ConfigNotFoundException("配置文件缺失", e);
} catch (JsonParseException e) {throw new ConfigParseException("配置格式错误", e);
}
}
4. JUnit5 参数化测试
@ParameterizedTest
@CsvSource({
"1, true", // 奇数用例
"2, false" // 偶数用例
})
void shouldCheckOddNumber(int input, boolean expected) {assertEquals(expected, NumberUtils.isOdd(input));
}
// 测试数据与逻辑分离
@ParameterizedTest
@MethodSource("provideUserAgeData")
void shouldValidateUserAge(int age, boolean valid) {assertEquals(valid, UserValidator.isAdult(age));
}
private static Stream<Arguments> provideUserAgeData() {
return Stream.of(Arguments.of(17, false), // 未成年
Arguments.of(18, true) // 刚成年
);
}
5. SLF4J 日志最佳实践
// 错误示范
logger.info("User" + username + "login from" + ip); // 字符串拼接开销
// 正确姿势(使用占位符)logger.info("User {} login from {}", username, ip);
// MDC 实现请求链路追踪
try (MDC.MDCCloseable closeable = MDC.putCloseable("traceId", UUID.randomUUID().toString())) {logger.debug("Start processing order");
orderService.process();
logger.debug("Order processed successfully");
}
避坑指南
代码坏味道自查清单
- [] 方法超过 50 行或圈复杂度 >10
- [] 存在以
tmp/data命名的变量 - [] catch 块中没有记录异常日志
- [] 测试类覆盖率 <70%
- [] 同一功能在多处重复实现
- [] 日志中存在 System.out.println
延伸思考题
- 当方法参数超过 5 个时,应该用什么 SOLID 原则进行优化?
- 如何通过日志分析定位接口性能瓶颈?
- 在微服务架构下,MDC 如何实现跨服务调用链追踪?
小贴士:在 IntelliJ 中安装
SonarLint插件,可以实时检测上述代码坏味道。
正文完
