十年前,当我第一次面对一个长达3000行的Java类文件时,那种窒息感至今记忆犹新。那是一个电商系统的订单处理模块,包含了从下单、支付到物流的所有逻辑,各种if-else嵌套深达7层,注释与代码严重脱节,变量名全是a1、a2这样的"密码"。当时的团队称之为"祖传代码",每个人都对它敬而远之,只敢在边缘修修补补,生怕动了哪根线导致整个系统崩溃。
这段经历让我深刻认识到:代码重构不是可选项,而是生存技能。就像外科医生需要掌握解剖学一样,程序员必须精通代码重构的艺术。但重构绝非简单的"整理代码",它本质上是对系统认知的重新塑造。好的重构应该像文艺复兴时期的雕塑家,能从粗糙的大理石中看出潜在的完美形态。
关键认知:重构的核心价值不在于让代码"看起来整洁",而在于降低系统的认知负荷。每次重构都应该让代码比之前更清晰地表达业务意图。
去年我主导了一个金融系统的重构项目,在开始前我们花了整整两周时间构建测试防护网。这个系统已经运行了5年,没有任何单元测试。我们的做法是:
java复制// 金融系统重构前的测试示例
@Test
public void shouldSettleFundsWithFeeCalculation() {
// 给定
FundSettlementService service = new FundSettlementService();
Transaction transaction = TestDataFactory.createTransaction(10000, "USD");
// 当
SettlementResult result = service.settle(transaction);
// 则
assertThat(result.getAmount()).isEqualTo(9800); // 2%手续费
assertThat(result.getStatus()).isEqualTo(SettlementStatus.COMPLETED);
}
这个测试虽然简单,但它锁定了系统最基本的行为契约。在后续重构中,这个测试帮我们捕获了3次意外引入的bug。
在物流跟踪系统的重构中,我们制定了严格的渐进式策略:
这种看似缓慢的方式,最终让我们在6个月内完成了整个系统的重构,期间系统始终保持可发布状态。
经过多年实践,我整理了一份更细致的坏味道检查清单:
| 坏味道类型 | 具体表现 | 危险等级 |
|---|---|---|
| 过大的类 | >300行或承担多个职责 | ★★★★ |
| 过长的参数列表 | >4个参数,尤其是基本类型 | ★★★ |
| 特性依恋 | 方法频繁访问另一个类的数据 | ★★★★ |
| 重复代码 | 相似代码段出现3次以上 | ★★★★ |
| 过度耦合 | 导入过多其他包中的类 | ★★★★ |
在电商促销系统中,我们遇到了这样的复杂条件判断:
java复制// 重构前
public Discount applyDiscount(Customer customer, Order order) {
if (customer != null) {
if (order != null) {
if (order.getItems() != null && !order.getItems().isEmpty()) {
if (customer.isVIP()) {
// 实际业务逻辑
} else if (customer.getJoinDate().before(SPECIAL_DATE)) {
// 另一个业务逻辑
}
}
}
}
return null;
}
我们不仅应用了卫语句,还引入了策略模式:
java复制// 重构后
public Discount applyDiscount(Customer customer, Order order) {
if (customer == null || order == null) return null;
if (CollectionUtils.isEmpty(order.getItems())) return null;
return DiscountStrategy.forCustomer(customer)
.calculateDiscount(order);
}
这个重构带来了三个显著改进:
在支付网关重构中,我们遇到了一个典型的switch-case问题:
java复制// 传统实现
public PaymentResult process(PaymentRequest request) {
switch (request.getChannel()) {
case "ALIPAY":
return new AlipayProcessor().process(request);
case "WECHAT":
return new WechatProcessor().process(request);
// 其他15个支付渠道...
}
}
我们采用Spring的自动发现机制进行了改造:
java复制// 策略接口
public interface PaymentProcessor {
String getChannelType();
PaymentResult process(PaymentRequest request);
}
// 具体实现(带注解)
@Component
public class AlipayProcessor implements PaymentProcessor {
@Override
public String getChannelType() { return "ALIPAY"; }
@Override
public PaymentResult process(PaymentRequest request) {
// 具体实现
}
}
// 工厂类
@Component
public class PaymentProcessorFactory {
private final Map<String, PaymentProcessor> processorMap;
@Autowired
public PaymentProcessorFactory(List<PaymentProcessor> processors) {
this.processorMap = processors.stream()
.collect(Collectors.toMap(
PaymentProcessor::getChannelType,
Function.identity()
));
}
public PaymentProcessor getProcessor(String channel) {
return Optional.ofNullable(processorMap.get(channel))
.orElseThrow(() -> new PaymentChannelNotSupportedException(channel));
}
}
这种实现带来了三个优势:
在库存管理系统重构中,我们遇到了典型的贫血模型问题:
java复制// 重构前
public class InventoryService {
public void adjustStock(Long itemId, int delta) {
Item item = itemRepository.findById(itemId);
if (item == null) throw new ItemNotFoundException();
int newStock = item.getStock() + delta;
if (newStock < 0) throw new InsufficientStockException();
item.setStock(newStock);
itemRepository.save(item);
if (newStock < item.getSafetyStock()) {
notificationService.sendLowStockAlert(item);
}
}
}
通过引入领域模型,我们将业务逻辑内聚到实体中:
java复制// 重构后
@Entity
public class Item {
@Id private Long id;
private int stock;
private int safetyStock;
public Item adjustStock(int delta) {
int newStock = this.stock + delta;
if (newStock < 0) {
throw new InsufficientStockException();
}
this.stock = newStock;
if (isBelowSafetyStock()) {
DomainEventPublisher.publish(new LowStockEvent(this));
}
return this;
}
public boolean isBelowSafetyStock() {
return stock < safetyStock;
}
}
// 服务层简化
public class InventoryService {
public void adjustStock(Long itemId, int delta) {
itemRepository.findById(itemId)
.orElseThrow(ItemNotFoundException::new)
.adjustStock(delta);
}
}
这个重构实现了:
在CRM系统重构中,我们建立了严格的命名规范:
| 命名场景 | 模式 | 示例 |
|---|---|---|
| 查询方法 | findBy+条件 | findOverdueInvoices |
| 命令方法 | 动词+名词 | renewSubscription |
| 状态判断 | is/has/can+形容词 | isEligibleForPromotion |
| 事件类 | 名词+过去分词 | OrderCancelled |
| DTO类 | 名词+DTO | CustomerDetailsDTO |
一个典型的改进案例:
java复制// 重构前
public List<Data> getData(Date d1, Date d2, int type) { ... }
// 重构后
public List<SalesReport> generateSalesReport(
DateRange reportingPeriod,
ReportGranularity granularity
) { ... }
我们制定了注释写作规范:
java复制// 坏注释示例
// 循环处理订单
for (Order order : orders) {
// 计算金额
double amount = order.getAmount();
// 保存
orderRepository.save(order);
}
// 好注释示例
// 使用最终价格更新订单(业务规则#BR-2043)
// 注意:必须在库存预留之后调用,否则会导致财务对账差异
orders.forEach(order -> {
double finalizedAmount = pricingService.calculateFinalPrice(order);
order.finalize(finalizedAmount);
orderRepository.save(order);
});
在UI组件库重构中,我们实施了严格的代码布局规范:
成员排序:
段落分隔:
链式调用:每行一个操作,点号开头
java复制// 重构前
public Flux<Order> processOrders(List<Order> orders) {
return Flux.fromIterable(orders)
.filter(order -> order.isValid()).flatMap(order -> inventoryService.reserveStock(order.getItems())).map(order -> paymentService.charge(order)).onErrorContinue((e, o) -> log.error("Failed to process order {}", o, e));
}
// 重构后
public Flux<Order> processOrders(List<Order> orders) {
return Flux.fromIterable(orders)
.filter(Order::isValid)
.flatMap(order -> inventoryService.reserveStock(order.getItems()))
.map(paymentService::charge)
.onErrorContinue((e, o) -> log.error("Failed to process order {}", o, e));
}
在最近的项目中,我们采用"三明治策略"处理重构与需求变更:
我们开发了一个简单的评估工具:
| 影响因素 | 低风险(1分) | 中风险(3分) | 高风险(5分) |
|---|---|---|---|
| 测试覆盖率 | >80% | 50-80% | <50% |
| 团队熟悉度 | 原作者在岗 | 部分了解 | 全新接手 |
| 依赖复杂度 | 独立模块 | 有限依赖 | 核心依赖 |
| 修改范围 | <100行 | 100-500行 | >500行 |
评分解读:
我们实践过的有效方法包括:
IntelliJ IDEA的重构功能:
Eclipse的JDT工具:
java复制// ArchUnit示例:禁止Service直接依赖DAO
@ArchTest
static final ArchRule service_should_not_depend_on_dao =
noClasses()
.that().resideInAPackage("..service..")
.should().dependOnClassesThat()
.resideInAPackage("..dao..");
在多年的重构实践中,我总结出三个认知层次:
一个典型的进阶路径是:
真正的重构大师不是技术最厉害的,而是最能理解业务本质的。他们能通过代码重构,让系统更清晰地表达业务意图,这才是重构的最高境界。