一、大型重构策略:梳理并分解继承体系、将过程化设计转化为对象设计、分离领域与表现层、提炼层次结构

大型重构,说白了就是给系统动大手术。我做了十几年架构,见过太多代码库从整洁变成一团乱麻。今天聊的这四个策略,是我在实际项目中反复用到的核心手段。

1. 梳理并分解继承体系

继承体系这东西,一开始看着挺美。子类继承父类,复用代码,多省事。但项目跑个两三年,你就会发现继承链越来越深,改一个基类,下面十几个子类全得跟着改。

我记得有个项目,继承深度达到了七层。最顶层的基类叫BaseEntity,底下有BusinessEntityAuditEntityVersionedEntity……到最后,新来的同事想加个字段,得先花两天搞清楚继承关系。

核心原则:组合优于继承。能用组合解决的问题,别硬上继承。

怎么分解?我一般分三步走:

  1. 识别"上帝基类"——那些被太多子类依赖的基类,优先处理
  2. 提取接口——把基类的行为抽象成接口,让子类各自实现
  3. 用组合替代继承——把公共逻辑拆成独立的服务类,通过依赖注入组合进来

举个例子,原来可能是这样:

// 重构前:继承体系
public class BaseRepository {
    protected Connection getConnection() { ... }
    protected void logQuery(String sql) { ... }
}

public class UserRepository extends BaseRepository {
    public User findById(int id) {
        Connection conn = getConnection();
        logQuery("SELECT * FROM users WHERE id = " + id);
        // ...
    }
}

重构后,变成这样:

// 重构后:组合替代继承
public class UserRepository {
    private final DatabaseService dbService;
    private final AuditService auditService;

    public UserRepository(DatabaseService db, AuditService audit) {
        this.dbService = db;
        this.auditService = audit;
    }

    public User findById(int id) {
        Connection conn = dbService.getConnection();
        auditService.logQuery("SELECT * FROM users WHERE id = " + id);
        // ...
    }
}
我的习惯:每次重构继承体系时,我会先画一张类图,把继承关系可视化。看到图里线条乱成一团,你就知道该动手了。

2. 将过程化设计转化为对象设计

过程化设计,就是那种一个函数几百行,全是if-elseswitch-case。你想想看,这种代码改起来多痛苦?加一个条件,得翻遍整个函数,生怕漏掉某个分支。

我曾经接手过一个支付系统,核心逻辑全写在一个PaymentProcessor类里,光process()方法就八百多行。里面处理了支付宝、微信、银联、PayPal……十几种支付方式。每次加新支付渠道,都得在这个方法里加一段if,测试覆盖率还特别低。

怎么转成对象设计?核心思路是把行为封装到对象里

  • 识别出不同的"行为变体"——比如不同的支付方式
  • 为每种变体创建一个类,实现统一的接口
  • 用工厂或策略模式来选择合适的实现

重构后的代码大概是这样的:

// 策略模式:每种支付方式一个类
public interface PaymentStrategy {
    PaymentResult pay(Order order);
}

public class AlipayStrategy implements PaymentStrategy {
    public PaymentResult pay(Order order) {
        // 支付宝特有的逻辑
    }
}

public class WechatStrategy implements PaymentStrategy {
    public PaymentResult pay(Order order) {
        // 微信支付特有的逻辑
    }
}

// 使用方只需要知道接口
public class PaymentService {
    private final Map<String, PaymentStrategy> strategies;

    public PaymentResult process(Order order, String channel) {
        PaymentStrategy strategy = strategies.get(channel);
        if (strategy == null) {
            throw new UnsupportedPaymentException(channel);
        }
        return strategy.pay(order);
    }
}
注意:别为了"面向对象"而面向对象。如果只有两三种变体,而且未来几乎不会增加,那用if-else反而更清晰。我见过有人把三个分支硬拆成五个类,结果代码更难懂了。

3. 分离领域与表现层

这个问题太常见了。很多项目一开始图省事,把业务逻辑直接写在控制器里。比如Spring MVC的Controller里,既有HTTP请求处理,又有数据库查询,还有业务规则校验。

我有个朋友的项目,Controller里直接写了SQL语句。你想想看,这要是换个数据库,得改多少个地方?

分离的核心是分层架构

层次 职责 常见问题
表现层 处理HTTP请求、参数校验、视图渲染 混入业务逻辑、直接操作数据库
应用层 编排业务用例、事务管理 过于臃肿、职责不清
领域层 核心业务规则、实体、值对象 被贫血模型替代、逻辑散落
基础设施层 数据库访问、外部服务调用 与领域层耦合过紧

重构时,我一般从识别"胖Controller"开始。如果一个Controller里超过50行业务逻辑,就该拆了。

// 重构前:Controller里混了业务逻辑
@RestController
public class OrderController {
    @PostMapping("/orders")
    public Order createOrder(@RequestBody OrderRequest request) {
        // 参数校验
        if (request.getAmount() <= 0) {
            throw new IllegalArgumentException("金额必须大于0");
        }
        // 业务逻辑:计算折扣
        double discount = 0;
        if (request.getAmount() > 1000) {
            discount = 0.1;
        }
        // 数据库操作
        Order order = new Order();
        order.setAmount(request.getAmount() * (1 - discount));
        orderRepository.save(order);
        return order;
    }
}
// 重构后:Controller只负责HTTP处理
@RestController
public class OrderController {
    private final OrderService orderService;

    @PostMapping("/orders")
    public Order createOrder(@RequestBody OrderRequest request) {
        return orderService.createOrder(request);
    }
}

// 领域服务处理业务逻辑
@Service
public class OrderService {
    private final OrderRepository orderRepository;
    private final DiscountCalculator discountCalculator;

    public Order createOrder(OrderRequest request) {
        request.validate();  // 参数校验
        double discount = discountCalculator.calculate(request.getAmount());
        Order order = new Order(request, discount);
        return orderRepository.save(order);
    }
}
避坑指南:我曾经把领域层拆得太细,每个小逻辑都单独一个类,结果类数量暴增,反而更难维护。后来我学乖了:先粗后细,先拆出大的模块,等确实需要时再细化。

4. 提炼层次结构

这个策略,说白了就是给代码"分层"。很多系统一开始没有明确的层次,所有代码混在一起。时间一长,你想改个功能,得先花半小时搞清楚代码在哪儿。

我常用的分层模式是四层架构

  • 入口层:接收外部请求,做最基础的校验
  • 编排层:协调多个领域服务,处理事务
  • 领域层:核心业务逻辑,不依赖外部框架
  • 基础设施层:数据库、消息队列、外部API

这里有个关键点:依赖方向必须单向。上层可以依赖下层,下层绝不能依赖上层。我见过一个项目,领域层里直接引用了Spring的@Autowired注解,结果想换个框架,整个领域层都得重写。

核心原则:依赖倒置。高层模块不应该依赖低层模块,两者都应该依赖抽象。

提炼层次结构时,我一般从最底层开始。先把基础设施层的代码抽出来,确保它们不依赖上层。然后逐步往上提炼。这样做的好处是,底层稳定了,上层再怎么改,底层都不用动。

嗯,这里要注意:别一次提炼太多层。我见过有人一口气提炼了七层,结果每层之间只有薄薄的一层封装,反而增加了复杂度。一般来说,三到四层就够用了。

四层架构依赖关系 入口层(Controller / API) 接收请求、参数校验、视图渲染 编排层(Service / Use Case) 事务管理、业务编排、协调领域服务 领域层(Domain / Entity) 核心业务规则、实体、值对象 基础设施层(DB / MQ / API) 依赖方向 上层依赖下层 下层不依赖上层

这张图展示的就是典型的四层依赖关系。箭头方向表示依赖方向,上层依赖下层,下层对上层一无所知。这样做的好处是,你想换数据库?只需要改基础设施层,领域层完全不用动。

我曾经踩过的坑:有次重构,我把所有层的代码都放在同一个模块里,结果编译时没问题,运行时才发现循环依赖。后来我强制要求:每个层次一个独立的模块,编译期就能发现依赖问题。

好了,这四个策略讲完了。总结一下:

  • 分解继承体系:用组合替代继承,减少耦合
  • 过程化转对象设计:把行为封装到对象里,用策略模式替代大if-else
  • 分离领域与表现层:Controller只做HTTP处理,业务逻辑交给领域服务
  • 提炼层次结构:明确分层,依赖方向单向,每层职责清晰

这些策略不是银弹,但在我经历过的项目中,它们确实帮了大忙。你想想看,如果你的代码库能按照这些原则重构一遍,维护成本至少能降一半。

公众号:蓝海资料掘金营,微信 deep3321