
大家好,我是卷毛。
前段时间接手了一个老项目,有个订单处理方法,if-else嵌套了7层,478行代码。我花了3天重构完,用的是什么?Java 21的模式匹配。
今天这篇文章,我把自己实际用到的5种重构手法分享出来,每种都有真实代码对比,保证你看完就能用。
这是真实代码,我只改了变量名:
复制代码// 重构前 —— 478行的怪物
public void processOrder(Order order) {
if (order != null) {
if (order.getType() == OrderType.NORMAL) {
if (order.getStatus() == OrderStatus.PENDING) {
if (order.getAmount() > 1000) {
if (order.getUser().getLevel() == UserLevel.VIP) {
// VIP大额待处理订单
if (order.getPaymentMethod() == PaymentMethod.WECHAT) {
vipWechatLargeOrderProcess(order);
} else if (order.getPaymentMethod() == PaymentMethod.ALIPAY) {
vipAlipayLargeOrderProcess(order);
} else {
vipOtherLargeOrderProcess(order);
}
} else {
// 普通用户大额待处理订单
normalLargeOrderProcess(order);
}
} else {
// 小额订单处理...
// 还有3层嵌套...
}
} else if (order.getStatus() == OrderStatus.PAID) {
// 已支付订单处理...
} else if (order.getStatus() == OrderStatus.CANCELLED) {
// 取消订单处理...
}
} else if (order.getType() == OrderType.GROUP) {
// 团购订单...
// 又是几层嵌套...
}
}
}
看完是不是血压上来了?别急,我们一步步来。
痛点:instanceof判断完还要强转,啰嗦。
复制代码// 以前
if (obj instanceof String) {
String str = (String) obj; // 多余的强转
System.out.println(str.length());
}// Java 21
if (obj instanceof String str) {
System.out.println(str.length());
}
实战场景:
复制代码// 处理不同类型的消息体
public void handleMessage(Object message) {
if (message instanceof TextMessage tm) {
textProcessor.process(tm.getContent());
} else if (message instanceof ImageMessage im) {
imageProcessor.process(im.getUrl(), im.getWidth(), im.getHeight());
} else if (message instanceof VideoMessage vm) {
videoProcessor.process(vm.getUrl(), vm.getDuration());
} else if (message instanceof null) {
log.warn("收到空消息");
}
}
一行代码省掉了声明+强转,10个分支就省10行。
痛点:基于类型的分支判断,if-else写起来又长又丑。
复制代码// 以前 —— 类型判断+强转,又臭又长
public double calculateArea(Shape shape) {
if (shape instanceof Circle) {
Circle c = (Circle) shape;
return Math.PI * c.getRadius() * c.getRadius();
} else if (shape instanceof Rectangle) {
Rectangle r = (Rectangle) shape;
return r.getWidth() * r.getHeight();
} else if (shape instanceof Triangle) {
Triangle t = (Triangle) shape;
double s = (t.getA() + t.getB() + t.getC()) / 2;
return Math.sqrt(s * (s - t.getA()) * (s - t.getB()) * (s - t.getC()));
} else {
throw new IllegalArgumentException("未知形状: " + shape.getClass().getName());
}
}// Java 21 —— switch模式匹配
public double calculateArea(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> {
double s = (t.a() + t.b() + t.c()) / 2;
yield Math.sqrt(s * (s - t.a()) * (s - t.b()) * (s - t.c()));
}
case null -> throw new IllegalArgumentException("形状不能为null");
default -> throw new IllegalArgumentException("未知形状");
};
}
注意:switch模式匹配要求穷举所有可能,如果你用了sealed class,编译器会帮你检查。
痛点:处理不同类型的消息/事件,DTO类一堆,处理逻辑if-else一堆。
先定义sealed接口和Record:
复制代码// 定义一个密封接口,限定实现类型
public sealed interface PaymentEvent permits PaymentSuccess, PaymentFailed, RefundProcessed {}public record PaymentSuccess(String orderId, BigDecimal amount, String tradeNo) implements PaymentEvent {}
public record PaymentFailed(String orderId, String reason, int retryCount) implements PaymentEvent {}
public record RefundProcessed(String orderId, BigDecimal refundAmount, String refundNo) implements PaymentEvent {}
处理逻辑:
复制代码// 简洁、类型安全、编译器保证穷举
public void handlePaymentEvent(PaymentEvent event) {
switch (event) {
case PaymentSuccess(String orderId, BigDecimal amount, String tradeNo) ->
orderService.confirmPayment(orderId, amount, tradeNo); case PaymentFailed(String orderId, String reason, int retryCount) when retryCount < 3 ->
retryService.scheduleRetry(orderId, retryCount + 1); case PaymentFailed(String orderId, String reason, int retryCount) ->
notifyService.notifyFailure(orderId, reason); case RefundProcessed(String orderId, BigDecimal refundAmount, String refundNo) ->
refundService.completeRefund(orderId, refundAmount, refundNo);
}
}
注意看 when retryCount < 3 这个guard条件——以前要用if嵌套实现的逻辑,现在一行搞定。
复制代码// Record组件可以直接解构
case PaymentSuccess(String orderId, BigDecimal amount, String tradeNo)// 也可以只绑定部分组件,用 _ 忽略不关心的
case PaymentSuccess(String orderId, _, _)// 甚至全忽略
case PaymentSuccess(_, _, _)
痛点:null检查散落各处,漏了一个就NPE。
复制代码// 以前 —— 各种null检查
public String getUserName(User user) {
if (user == null) {
return "匿名用户";
}
if (user.getProfile() == null) {
return user.getName();
}
if (user.getProfile().getNickname() == null) {
return user.getName();
}
return user.getProfile().getNickname();
}// Java 21 —— null模式匹配
public String getUserName(User user) {
return switch (user) {
case null -> "匿名用户";
case User u when u.profile() == null -> u.name();
case User u when u.profile().nickname() == null -> u.name();
case User u -> u.profile().nickname();
};
}
配合Optional更优雅:
复制代码public String getUserName(User user) {
return Optional.ofNullable(user)
.map(User::getProfile)
.map(Profile::getNickname)
.orElseGet(() -> Optional.ofNullable(user)
.map(User::getName)
.orElse("匿名用户"));
}
用上面4种手法组合,478行变62行:
复制代码// 重构后 —— 62行,清晰可维护
public void processOrder(Order order) {
switch (order) {
case null -> log.warn("收到空订单"); case Order o when o.type() == OrderType.NORMAL ->
processNormalOrder(o); case Order o when o.type() == OrderType.GROUP ->
processGroupOrder(o);
}
}private void processNormalOrder(Order order) {
switch (order) {
case Order(var id, _, var amount, var status, var user, var payment)
when status == OrderStatus.PENDING && amount.compareTo(BigDecimal.valueOf(1000)) > 0
&& user.level() == UserLevel.VIP ->
processVipLargeOrder(order, payment); case Order(_, _, var amount, var status, var user, _)
when status == OrderStatus.PENDING && amount.compareTo(BigDecimal.valueOf(1000)) > 0 ->
normalLargeOrderProcess(order); case Order(_, _, _, var status, _, _) when status == OrderStatus.PAID ->
paidOrderProcess(order); case Order(_, _, _, var status, _, _) when status == OrderStatus.CANCELLED ->
cancelledOrderProcess(order); default -> pendingSmallOrderProcess(order);
}
}private void processVipLargeOrder(Order order, PaymentMethod payment) {
switch (payment) {
case WECHAT -> vipWechatLargeOrderProcess(order);
case ALIPAY -> vipAlipayLargeOrderProcess(order);
default -> vipOtherLargeOrderProcess(order);
}
}
478行 → 62行,而且每一行都在说"做什么",而不是"怎么判断"。
| 维度 | 重构前 | 重构后 |
|---|---|---|
| 代码行数 | 478行 | 62行 |
| 最大嵌套层级 | 7层 | 2层 |
| 圈复杂度 | 34 | 8 |
| 新增分支成本 | 在大方法里加if-else | 加一个case |
| 可测试性 | 差,一个方法测所有分支 | 好,每个子方法独立测试 |
| 可读性 | 需要跟踪嵌套逻辑 | 顺序阅读,一目了然 |
when后面超过2个条件就考虑提取方法模式匹配不是什么"新潮语法糖",它是Java在向函数式编程范式靠拢的重要一步。用了之后你会发现,代码不只是变短了,而是变清晰了——你在表达"做什么",而不是"怎么做"。
9年Java开发,我越来越觉得:好代码不是写得巧妙,而是写得让人一看就懂。