"最好的代码不是一次写对的,而是不断重构出来的。" ------ Martin Fowler
前言:技术债务带来的效能瓶颈
我所在的团队负责一个多渠道回收业务系统,接入了十几个外部渠道。每个渠道都有自己独特的协议规范、业务流程和特殊要求。随着业务发展,系统逐渐的遇到了一系列问题:
- 代码膨胀:单个策略类代码超过800行,包含大量if-else判断,可读性极差
- 重复代码:不同渠道间有70%的代码逻辑相似,有很多都是逻辑类似,但参数名称、结构不一致,难以复用
- 难以测试:业务逻辑与协议处理耦合,单元测试覆盖率不到30%,主要依赖人工测试
- 维护困难:改一个渠道的逻辑,需要理解整个类的800行代码,调研成本极高
技术债务不仅影响代码质量,更直接冲击了团队的研发效能。我们在日常开发中遭遇了三个核心瓶颈:
① 新人上手困难,培养周期长
新同学加入团队后,需要2-3个月才能理解现有代码逻辑:
- 单个策略类800+行,找一个业务逻辑点要翻半天
- 协议处理、业务逻辑、数据操作混在一起,理不清头绪
- 没有清晰的架构文档,只能靠"师傅带徒弟"口口相传
② 需求承接效率低,开发成本高
每次接入新渠道,都是一次"复制粘贴+魔改"的过程:
- 复制一个相似渠道的策略类
- 对照需求文档,逐个方法修改参数、逻辑
- 开发写入个性化逻辑
③ 测试回归成本高,质量保障困难
代码耦合导致"牵一发而动全身":
- 修改一个渠道的逻辑,可能影响其他渠道,测试范围难以评估
- 单元测试覆盖率低(30%),主要靠人工回归测试
- 每次发版前,测试同学要回归受影响渠道的核心流程
此时我的脑子里出现了一个想法:为了早点下班~ 必须重构了!
一、 重构方案设计
针对上述的三大痛点,我们很快制定了初步的重构规划:
| 痛点 | 重构目标 | 预期效果 |
|---|---|---|
| 新人上手慢 | 架构清晰化、职责单一化 | 培养周期从3周降到1周,调研成本降低70% |
| 需求效率低 | 通用逻辑可复用、新增渠道可配置化 | 新渠道接入成本降低70% |
| 测试成本高 | 解耦分层、单元测试覆盖率≥80% | 回归测试时间减半,Code Review时间降低80% |
三大技术策略:
- 分层解耦:将协议层、业务层、数据层清晰分离,让新人快速定位代码
- 责任链模式:每个Handler职责单一,可独立开发、测试、复用
- 适配器模式:新渠道只需实现Adapter,组装Handler即可
目标有了,问题也来了:这是一个对接10余个活跃渠道的业务系统,系统复杂,且每日有数百万请求打进系统,如何在不影响线上业务的前提下进行重构?
渐进式落地,而非一步到位
出于对生产环境的敬畏之心,我们不敢"一刀切"地推倒重来。经过评估,我们选择了渐进式重构策略: 整体改进是一个持续的过程,目标清晰但落地需要时间。我们会逐步验证架构可行性,再慢慢进行迁移,确保每一步都是稳定可控的。
阶段规划:
| 阶段 | 时间 | 目标 |
|---|---|---|
| 第一阶段 | 1-2周 | 架构验证:完成1个渠道的规范化改造 |
| 第二阶段 | 1-2个月 | 核心迁移:完成50%渠道的迁移 |
| 第三阶段 | 3-6个月 | 全量迁移:剩余渠道全部迁移 |
| 第四阶段 | 持续优化 | 补充协议Handler、完善监控 |
关键原则
-
新老并行:新架构与旧代码并存,通过配置开关灰度切换
-
小步验证:每完成一个渠道,立即上线验证,确保稳定后再推进下一个
-
数据对比:新老架构同时执行,对比结果一致性,发现问题及时回滚
二、 基于PDCA思想驱动的人机协作模式
PDCA(Plan-Do-Check-Act)是戴明环的核心思想,特别适合用于:
- 复杂系统的渐进式改进:将大目标拆解为小任务
- 风险可控的迭代验证:每个小步骤都可验证
- 知识沉淀与持续优化:从单次需求到全局改进
在与AI协作时,PDCA的价值更加明显:
| PDCA阶段 | 人的职责 | AI的职责 | 协作模式 |
|---|---|---|---|
| Plan | 架构设计、任务拆解、确定路径 | 提供设计建议、分析技术方案 | 人主导,AI辅助 |
| Do | 编写核心逻辑、Review代码 | 生成模板代码、实现细节 | 结对编程 |
| Check | 验证业务逻辑、集成测试 | 生成单元测试、代码检查 | 人验证,AI执行 |
| Act | 总结经验、沉淀规则 | 整理文档、提取模式 | 共同迭代 |
三、 实战案例:渠道统一Handler架构重构
3.1 Plan阶段:架构设计与任务拆解
3.1.1 原有架构的痛点分析
重构前,我们的代码结构是这样的:
java
@Service("kaChannelBusinessStrategy")
public class KaChannelBusinessStrategy extends BaseChannelBusinessStrategy {
public EnquiryPriceResult getEnquiryPrice(...) {
// 1. 参数校验(50行)
// 2. 解密验签(30行)
// 3. 获取渠道配置(40行)
// 4. 调用报价服务(60行)
// 5. 计算佣金(50行)
// 6. 组装响应(30行)
// 7. 加密签名(20行)
// ... 总计500+行
}
public CreateOrderResult createRecycleOrder(...) {
// 同样的模式,又是几百行...
}
}
问题总结:
- 职责不清:一个类既处理协议,又处理业务,还管数据
- 代码重复:AChannelBusinessStrategy、BChannelBusinessStrategy里有大量相似代码
- 难以扩展:新增一个渠道,需要复制粘贴800行代码
- 测试困难:依赖过多,mock成本高
3.1.2 新架构设计思路
本次我们的设计,是一个基于责任链模式的新架构:

核心设计点:
-
分层解耦
- Facade层:统一入口,参数校验、异常处理
- Adapter层:渠道适配,协议转换
- Handler层:责任链模式,每个Handler职责单一
-
可复用的Handler DuplicationCheckHandler → 幂等性检查(通用) ChannelConfigHandler → 渠道配置获取(通用) ReportMappingHandler → 报告映射(通用) OrderParamAssemblyHandler → 参数组装(可定制) OrderCreationHandler → 订单创建(通用)
-
灵活的编排机制
java// A渠道:标准流程 chain.addHandler(duplicationCheck) .addHandler(channelConfig) .addHandler(reportMapping) .addHandler(orderParamAssembly) .addHandler(orderCreation); // KA渠道:额外增加权限校验 chain.addHandler(duplicationCheck) .addHandler(kaAuthCheck) // KA特有 .addHandler(channelConfig) .addHandler(reportMapping) .addHandler(orderParamAssembly) .addHandler(orderCreation);
3.1.3 任务拆解
有了架构设计后,我将重构任务拆解为8个小任务:
| 任务ID | 任务描述 | 依赖关系 |
|---|---|---|
| T1 | 定义责任链上下文数据结构 | - |
| T2 | 定义IChannelAdapter接口 | T1 |
| T3 | 定义BusinessHandler接口 | T1 |
| T4 | 实现5个通用Handler | T3 |
| T5 | 实现统一对外接口 | T2 |
| T6 | 实现ChannelAdapter | T2, T4 |
| T7 | 编写单元测试 | T4-T6 |
| T8 | 集成测试与灰度发布 | T7 |
关键点 :每个任务都是独立可验证的,这也为后续与Cursor协作奠定了基础。
3.2 Do阶段:与Cursor协同编码
3.2.1 建立清晰的上下文
在开始编码前,我做了三件事:
1、 用自然语言写了一个详细的架构说明文档
AI更擅长理解结构化信息,为了提升Cursor的代码生成质量,我首先为Cursor量身定制了一份"产品需求文档"(PRD)。有了这份文档,Cursor代码生成的有效率大大提升,大部分时间,我只需要微调业务细节,就可以直接使用它生成的代码。
包含:
- 架构全景图
- 核心设计模式(责任链、适配器、模板方法、策略)
- 数据流转过程(Context如何在Handler间传递)
- 扩展点说明(如何新增渠道、Handler、Action)
2、 定义好接口和数据结构
java
/**
* 渠道上下文
*/
@Data
@Builder
public class ChannelContext {
private String requestId; // 请求ID
private String channelCode; // 渠道编码
private String action; // 业务动作
private Object bizData; // 业务数据
private Map<String, Object> handlerResults; // Handler结果
// ...
}
3、 编写第一个Handler作为示例
我手写了一个DuplicationCheckHandler,作为Cursor的参考模板:
java
@Component
public class DuplicationCheckBusinessHandler
extends AbstractTypedBusinessHandler<CreateOrderBizData, Boolean> {
@Override
protected Boolean handleTyped(CreateOrderBizData input, ChannelContext context) {
// 1. 根据outOrderNo查询订单
RecycleOrderVO existOrder = orderService.queryByOutOrderNo(input.getOutOrderNo());
// 2. 如果订单已存在,返回true(跳过后续Handler)
if (existOrder != null) {
log.info("订单已存在,跳过创建流程, outOrderNo={}", input.getOutOrderNo());
context.putHandlerResult("ORDER_EXISTS", existOrder);
return true;
}
return false;
}
@Override
public int getOrder() {
return 100; // 最先执行
}
}
完成了上述步骤后,我开始与Cursor结对编程。
参考DuplicationCheckBusinessHandler,帮我实现ChannelConfigBusinessHandler,用于获取渠道配置。需要满足:
- 从ExternalChannelConfigService获取配置
- 从PricedSourceService获取报价源配置
- 将结果保存到context的handlerResults中
- order设置为200
Cursor生成的代码:
java
@Component
public class ChannelConfigBusinessHandler
extends AbstractTypedBusinessHandler<CreateOrderBizData, ExternalChannelConfig> {
@Resource
private ExternalChannelConfigService configService;
@Resource
private PricedSourceService pricedSourceService;
@Override
protected ExternalChannelConfig handleTyped(CreateOrderBizData input, ChannelContext context) {
String channelCode = context.getChannelCode();
// 1. 获取渠道配置
ExternalChannelConfig channelConfig = configService.getByChannelCode(channelCode);
if (channelConfig == null) {
throw new ChannelException("CHANNEL_NOT_FOUND", "渠道配置不存在: " + channelCode);
}
// 2. 获取报价源配置
PricedSourceBO pricedSource = pricedSourceService.getPricedSource(
channelConfig.getOrgId(), input.getCategoryId());
// 3. 保存到上下文
context.putHandlerResult("CHANNEL_CONFIG", channelConfig);
context.putHandlerResult("PRICED_SOURCE", pricedSource);
log.info("获取渠道配置成功, channelCode={}, orgId={}",
channelCode, channelConfig.getOrgId());
return channelConfig;
}
@Override
public int getOrder() {
return 200;
}
}
可以看到,代码结构完全符合预期,异常处理到位,日志也很规范。
3.2.3 快速迭代的秘诀
在Do阶段,我总结出了几个与Cursor高效协作的技巧:
技巧1:更加明确的提示
不好的提示词:
帮我实现所有的Handler
好的提示词:
先实现DuplicationCheckHandler,用于检查订单是否已存在
技巧2:提供参考代码
参考DuplicationCheckHandler的实现方式,生成ChannelConfigHandler
技巧3:明确约束条件
markdown
要求:
1. 继承AbstractTypedBusinessHandler
2. order设置为200
3. 必须包含日志
4. 异常使用ChannelException
技巧4:及时Check
每生成一个Handler,我都会:
- 检查是否符合接口规范
- 运行单元测试
- 代码Review(看命名、异常处理、日志)
3.3 Check阶段:代码审查与验证
3.3.1 多层次的检查机制
在Check阶段,我建立了三层验证机制:
第一层:IDE即时检查
- Cursor的智能提示会提醒语法错误
- SonarLint插件实时检查代码质量
第二层:单元测试
我让Cursor帮我生成了单元测试:
为DuplicationCheckHandler生成单元测试,需要覆盖:
- 订单不存在的场景
- 订单已存在的场景
- 查询异常的场景
Cursor生成的测试:
java
@RunWith(SpringRunner.class)
@ContextConfiguration("classpath:scf-spring-test.xml")
public class DuplicationCheckBusinessHandlerTest {
@Resource
private DuplicationCheckBusinessHandler handler;
@Resource
private IRecycleOrderService orderService;
@Test
public void testHandle_OrderNotExists() {
// 构建测试数据
CreateOrderBizData input = CreateOrderBizData.builder()
.outOrderNo("OUT_ORDER_001")
.channelCode("TEST_CHANNEL")
.productId(123456L)
.build();
ChannelContext context = ChannelContext.builder()
.requestId("REQ_001")
.channelCode("TEST_CHANNEL")
.action("CREATE_ORDER")
.bizData(input)
.build();
// 执行
Boolean result = handler.handleTyped(input, context);
// 验证
System.out.println("订单不存在场景测试结果: " + result);
System.out.println("Context中的ORDER_EXISTS: " + context.getHandlerResult("ORDER_EXISTS"));
}
@Test
public void testHandle_OrderExists() {
// 构建测试数据 - 订单已存在的情况
CreateOrderBizData input = CreateOrderBizData.builder()
.outOrderNo("OUT_ORDER_002") // 这个订单号在数据库中已存在
.channelCode("TEST_CHANNEL")
.productId(123456L)
.build();
ChannelContext context = ChannelContext.builder()
.requestId("REQ_002")
.channelCode("TEST_CHANNEL")
.action("CREATE_ORDER")
.bizData(input)
.build();
// 执行
Boolean result = handler.handleTyped(input, context);
// 验证
RecycleOrderVO existOrder = (RecycleOrderVO) context.getHandlerResult("ORDER_EXISTS");
System.out.println("订单已存在场景测试结果: " + result);
System.out.println("已存在订单信息: " + JsonUtils.toJsonWithoutNull(existOrder));
}
}
第三层:集成测试
我在测试环境跑了完整的下单流程,验证Handler链的协作:
java
@RunWith(SpringRunner.class)
@ContextConfiguration("classpath:scf-spring-test.xml")
public class ChannelUnifiedFacadeTest {
@Resource
private IChannelUnifiedFacade channelUnifiedFacade;
@Resource
private IRecycleOrderService orderService;
@Test
public void testCreateOrder_FullProcess() {
// 构建完整的渠道请求
CreateOrderBizData bizData = CreateOrderBizData.builder()
.outOrderNo("TEST_OUT_ORDER_" + System.currentTimeMillis())
.channelCode("KA")
.productId(123456L)
.categoryId(1L)
.skuId(1001L)
.userId(888888L)
.picList(Collections.singletonList(
Pic.builder().picUrl("https://test.com/pic1.jpg").build()
))
.build();
ChannelRequest<CreateOrderBizData> request = ChannelRequest.<CreateOrderBizData>builder()
.requestId("REQ_" + System.currentTimeMillis())
.channelCode("KA")
.action("CREATE_ORDER")
.bizData(bizData)
.build();
// 执行完整流程
ChannelResponse<Long> response = channelUnifiedFacade.process(request);
// 打印结果
System.out.println("=== 集成测试结果 ===");
System.out.println("请求成功: " + response.isSuccess());
System.out.println("订单ID: " + response.getData());
System.out.println("响应码: " + response.getCode());
System.out.println("响应信息: " + response.getMessage());
// 验证订单确实创建成功
if (response.getData() != null) {
RecycleOrderVO order = orderService.getById(response.getData());
System.out.println("订单详情: " + JsonUtils.toJsonWithoutNull(order));
System.out.println("订单状态: " + order.getOrderState());
System.out.println("外部订单号: " + order.getOutOrderNo());
}
}
}
3.4 Act:规则沉淀与持续优化
3.4.1 沉淀提示词模板
经过多次迭代,我总结出了一套可复用的提示词模板:
模板1:生成Handler
scss
角色:你是一个Java后端工程师,熟悉责任链模式
任务:实现一个BusinessHandler,用于[具体功能]
要求:
1. 继承AbstractTypedBusinessHandler<P, R>
2. 输入类型是[P],输出类型是[R]
3. order值设置为[ORDER_VALUE]
4. 从context中获取[DEPENDENCY]
5. 将结果保存到context.putHandlerResult("[RESULT_KEY]", result)
6. 异常使用ChannelException,格式:new ChannelException(errorCode, message)
7. 添加日志:处理开始、处理成功、处理失败
参考代码:[粘贴参考Handler代码]
模板2:生成Adapter
less
角色:你是一个Java架构师,熟悉适配器模式和责任链模式
任务:实现[渠道名]ChannelAdapter
要求:
1. 继承AbstractIChannelAdapter
2. 注入以下Handler:[列出Handler列表]
3. 根据action构建不同的Handler链:
- CREATE_ORDER: [Handler顺序]
- UPDATE_ORDER: [Handler顺序]
4. 添加@Component注解,value为渠道code,用于自动注入
5. 添加完善的日志
参考代码:[粘贴AChannelAdapter代码]
3.4.2 持续优化
在重构过程中,我发现了一个可以提取的通用模式:参数校验。
最初,每个Handler都自己做参数校验:
java
// DuplicationCheckHandler
if (StringUtils.isBlank(input.getOutOrderNo())) {
throw new ChannelException("PARAM_ERROR", "外部订单号不能为空");
}
// ChannelConfigHandler
if (StringUtils.isBlank(input.getChannelCode())) {
throw new ChannelException("PARAM_ERROR", "渠道编码不能为空");
}
在Act阶段,我提取了一个通用的ParamValidationHandler:
java
@Component
public class ParamValidationBusinessHandler
extends AbstractTypedBusinessHandler<Object, Void> {
@Override
protected Void handleTyped(Object input, ChannelContext context) {
//.....
return null;
}
@Override
public int getOrder() {
return 50;
}
}
现在,只需要在Handler链中加入这个Handler,就能统一处理参数校验了。
最后的话
这篇文章分享的是我们正在进行的探索 ,而非一个 "完美的成功案例" 。
我们的重构之旅才走了一小步,还有很多坑要踩,很多问题要解决。但我们选择边做边分享,因为:
真实的经验比完美的故事更有价值。
愿你在AI时代,既能拥抱新工具提升效率,又能保持对代码质量的追求、对架构美感的坚持。
关于作者,于天奇,侠客汇Java开发工程师。 想了解更多转转公司的业务实践,欢迎点击关注下方公众号
转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。 关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~