# 如何提升代码健壮性
工作近三个月,明显感受到自己写的代码臃肿、适配性差。尽管有公司项目旧、无规范的原因,但是自身的代码也有很大问题。所以问了ai如何提升代码健壮性。以下。
# 早返回(Guard Clause)
问题代码:代码不要层层嵌套,往往读到底才能了解代码目的。
public User createUser(CreateUserDto dto) { if (dto != null) { if (dto.getName() != null && !dto.getName().isBlank()) { if (dto.getAge() != null && dto.getAge() >= 18) { User user = new User(); user.setName(dto.getName().trim()); user.setAge(dto.getAge()); return userRepository.save(user); } else { throw new IllegalArgumentException("年龄必须≥18"); } } else { throw new IllegalArgumentException("姓名不能为空"); } } else { throw new IllegalArgumentException("dto 不能为空"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18重构后:非法场景提前 return,主干逻辑顶格。
public User createUser(CreateUserDto dto) { if (dto == null) throw new IllegalArgumentException("dto 不能为空"); if (dto.getName() == null || dto.getName().isBlank()) throw new IllegalArgumentException("姓名不能为空"); if (dto.getAge() == null || dto.getAge() < 18) throw new IllegalArgumentException("年龄必须≥18"); User user = new User(); user.setName(dto.getName().trim()); user.setAge(dto.getAge()); return userRepository.save(user); }
1
2
3
4
5
6
7
8
9
10
11
12单测(JUnit 5 + assertThrows):
@Test void shouldRejectNullDto() { assertThrows(IllegalArgumentException.class, () -> service.createUser(null), "dto 不能为空"); }
1
2
3
4
5
6
# 表驱动消灭 if/else 链
问题代码:业务分支一多就膨胀。
// 啰嗦 public BigDecimal discount(OrderType type, BigDecimal amount) { if (type == OrderType.NORMAL) return amount.multiply(new BigDecimal("0.95")); else if (type == OrderType.VIP) return amount.multiply(new BigDecimal("0.90")); else if (type == OrderType.SVIP) return amount.multiply(new BigDecimal("0.85")); else throw new IllegalArgumentException("未知类型"); }
1
2
3
4
5
6
7重构后:Enum 自带行为,O(1) 查表,后续加类型只改枚举。
enum OrderType { NORMAL(BigDecimal.valueOf(0.95)), VIP(BigDecimal.valueOf(0.90)), SVIP(BigDecimal.valueOf(0.85)); private final BigDecimal rate; OrderType(BigDecimal rate) { this.rate = rate; } public BigDecimal apply(BigDecimal amount) { return amount.multiply(rate); } } public BigDecimal discount(OrderType type, BigDecimal amount) { Objects.requireNonNull(type, "type 不能为空"); return type.apply(amount); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 重复校验逻辑集中化
问题代码:每个接口都手写非空、长度、范围判断。
// 啰嗦,到处复制 if (name == null || name.length() < 2 || name.length() > 20) throw new IllegalArgumentException("姓名长度 2-20");
1
2
3重构后:封装校验器 + 自定义异常,统一错误码。
public final class Validator { private Validator() {} public static String validName(String name) { if (name == null || name.length() < 2 || name.length() > 20) throw new BizException(400, "NAME_INVALID", "姓名长度 2-20"); return name; } } // 使用 user.setName(Validator.validName(dto.getName()));
1
2
3
4
5
6
7
8
9
10
11
# 异常体系混乱
问题代码:直接抛出异常,无法判断异常来自用户还是服务器。
重构后:定义两类异常 +
@ControllerAdvice
统一转 JSON。// 业务异常,前端 4xx public class BizException extends RuntimeException { private final int code; public BizException(int code, String message) { super(message); this.code = code; } public int getCode() { return code; } } // 系统异常,前端 5xx public class SysException extends RuntimeException { public SysException(String message, Throwable cause) { super(message, cause); } } @RestControllerAdvice public class GlobalAdvice { @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(BizException.class) public Map<String, Object> handleBiz(BizException e) { return Map.of("code", e.getCode(), "msg", e.getMessage()); } @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(SysException.class) public Map<String, Object> handleSys(SysException e) { return Map.of("code", 500, "msg", "系统繁忙,请稍后重试"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 魔法值硬编码 → 枚举 + 常量
问题代码:直接使用String硬编码
if ("PAY_SUCCESS".equals(status)) { ... }
1重构后:使用枚举+常量
enum PayStatus { SUCCESS, FAIL, CLOSED } if (PayStatus.SUCCESS == status) { ... }
1
2
# 过度 try-catch → 利用 Optional + 自定义异常
问题代码:到处捕获 NPE,代码臃肿。
try { return order.getItem().getProduct().getPrice(); } catch (NullPointerException e) { return BigDecimal.ZERO; }
1
2
3
4
5重构后:Optional 链式 + orElseThrow。
return Optional.ofNullable(order) .map(Order::getItem) .map(Item::getProduct) .map(Product::getPrice) .orElseThrow(() -> new BizException(400, "PRICE_NOT_FOUND", "价格缺失"));
1
2
3
4
5
# 日志无重要信息
重构后:使用 Slf4j + MDC,ELK 可直接索引,打印出关键的变量。
MDC.put("orderNo", order.getOrderNo()); log.info("订单处理完成, amount={}", order.getAmount()); MDC.clear();
1
2
3