交易分层利器:模板方法与工厂模式实践

交易分层利器:模板方法与工厂模式实践

交易处理是许多业务系统的核心,尤其是在电商、金融等领域。当交易类型和业务逻辑日益复杂时,代码也常常陷入臃肿、难以维护的困境。我之前参与的一个交易系统,就采用了模板方法模式与工厂模式的组合,将交易流程梳理得井井有条,兼顾了复用、扩展性和可读性。这套设计非常值得分享。


为何交易逻辑总是"打架"?

在许多交易系统中,我常看到以下几个"老大难"问题:

  • 所有逻辑"一锅烩":一个Service类包揽所有交易类型,代码量像滚雪球一样膨胀,维护起来心力交瘁。
  • 细节混淆,复用困难:不同交易类型(如钱包支付、微信支付)虽然主流程相似(校验、扣款、入库),但具体实现细节缠绕在一起,导致代码难以复用。
  • 牵一发而动全身:修改一种交易逻辑,常常会意外影响其他交易,引发连锁bug。
  • 重复代码"占领"高地:为了应付不同的交易类型,复制粘贴成了常态,调试和测试成本也随之飙升。

这些问题,归根结底是流程骨架与实现细节耦合过深。如果能把固定的流程抽象出来,把变化的细节做成可插拔的组件,开发体验和系统可维护性都能得到极大改善。


模板+工厂:交易处理的分层之道

以电商购物车下单为例,用户可能选择钱包支付、支付宝支付或微信支付等多种方式。这套设计将交易处理划分为三层:

✅ 第一层:统一入口

这一层负责处理所有交易类型的通用逻辑,比如权限校验参数校验等。校验通过后,它会将请求"分发"给对应的具体交易处理器:

java 复制代码
combineOrderFactory.get(bizType).doCombineHandler(request);

瞧,一行代码就能根据业务类型(bizType)获取并调用相应的交易处理模板。这里的combineOrderFactory正是工厂模式的体现。

✅ 第二层:业务逻辑层(抽象流程骨架)

这一层运用模板方法模式,定义了交易的核心流程,就好比搭好了整个交易处理的"骨架"。例如:

  • execute():总的交易执行阶段。
  • prepareExecute():交易前的准备工作。
  • postExecute():交易后的收尾处理(可选)。

通过抽象方法和钩子方法,它规定了交易必须遵循的步骤,同时允许子类根据需要重写或扩展某些步骤。

✅ 第三层:交易执行细节层(具体实现)

这一层是具体交易类型实现差异的地方。它将第二层定义的抽象步骤进一步细化,并由不同的子类来实现。例如,prepareExecute()可以包含:

  • verification():具体的业务校验,比如检查库存、用户状态等。
  • saveOrder():保存订单记录到数据库。

而核心的execute()方法,则可以拆解成更精细的步骤,以便子类按需实现:

  • beforeTransaction():预处理,比如冻结用户余额。
  • handleFloatAmount():处理浮动金额,比如计算手续费或优惠。
  • transaction():最核心的交易逻辑,例如真正的扣款操作。
  • afterTransaction():交易后的处理,比如发送交易成功通知。

模板方法模式伪代码示例

java 复制代码
public abstract class AbstractTransactionTemplate {

    // final方法,定义了不可改变的交易主流程
    public final void process(Request request) {
        verification(request); // 钩子方法,子类可选择实现
        saveOrder(request);    // 钩子方法,子类可选择实现
        beforeTransaction(request); // 钩子方法
        handleFloatAmount(request); // 钩子方法
        transaction(request);   // 抽象方法,必须由子类实现
        afterTransaction(request);  // 钩子方法
    }

    // 默认空实现,子类可按需重写
    protected void verification(Request request) {}
    protected void saveOrder(Request request) {}
    protected void beforeTransaction(Request request) {}
    protected void handleFloatAmount(Request request) {}
    
    // 核心交易逻辑,必须由具体的交易类型实现
    protected abstract void transaction(Request request);
    
    protected void afterTransaction(Request request) {}
}

工厂模式:自动化注册与获取模板

为了让不同的交易类型能够灵活地被调用,这里结合Spring容器,利用注解实现模板的自动注册:

java 复制代码
// 1. 定义一个注解,用于标识不同的交易类型
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TransactionType {
    String value();
}
java 复制代码
// 2. 实现工厂类,扫描并注册带有@TransactionType注解的模板
public class TransactionTemplateFactory implements ApplicationContextAware, InitializingBean {

    private static final Map<String, AbstractTransactionTemplate> templateMap = new HashMap<>();
    private ApplicationContext applicationContext;
    private static final Logger log = LoggerFactory.getLogger(TransactionTemplateFactory.class);

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.applicationContext = context;
    }

    @Override
    public void afterPropertiesSet() {
        // 获取Spring容器中所有AbstractTransactionTemplate的子类
        Map<String, AbstractTransactionTemplate> beans = applicationContext.getBeansOfType(AbstractTransactionTemplate.class);
        for (AbstractTransactionTemplate bean : beans.values()) {
            // 检查是否有@TransactionType注解
            TransactionType annotation = bean.getClass().getAnnotation(TransactionType.class);
            if (annotation != null) {
                String type = annotation.value();
                templateMap.put(type, bean); // 将类型与模板实例映射
            } else {
                log.warn("TransactionTemplate {} 未声明 @TransactionType 注解,已跳过注册", bean.getClass().getSimpleName());
            }
        }
    }

    // 静态方法,根据类型获取对应的模板实例
    public static AbstractTransactionTemplate get(String type) {
        return templateMap.get(type);
    }
}

使用起来非常简洁:

java 复制代码
// 根据请求中的类型,获取对应的交易模板
AbstractTransactionTemplate template = TransactionTemplateFactory.get(request.getType());
// 调用模板的process方法,执行整个交易流程
template.process(request);

这样,每当增加一种新的交易类型(比如"优惠券支付"),只需要新增一个类继承AbstractTransactionTemplate,实现其抽象方法和需要定制的钩子方法,并加上@TransactionType("优惠券支付")注解,无需修改现有代码,就能自动注册并被系统识别。


这样的分层设计是否"过度"了?

有人可能会觉得,这样分两层是不是有点复杂?直接把所有步骤都写在一个执行类里不也行吗?

这确实是一个值得思考的问题。在实际开发中,我们不必一开始就"过度设计"。可以先从一个相对精简的结构开始,比如只用一个执行类。但当发现不同交易类型的流程逐渐趋于统一,而细节差异却日益明显时,再考虑抽象出模板层,将流程骨架和实现细节分离,往往能事半功倍。


总结

优秀的系统设计并非一蹴而就,而是在项目演进中不断提炼出来的。模板方法模式与工厂模式的组合,为交易处理这类流程固定、细节多样的场景提供了一种优雅的解决方案。它帮助我们将复杂的交易流程骨架与多变的实现差异清晰地表达出来,让代码既易读又易扩展,显著提升了系统的可维护性。

希望这套实践能为你提供一些启发。你在类似的交易场景下,有什么独到的设计经验吗?欢迎留言分享!

相关推荐
linweidong5 小时前
Go开发简历优化指南
分布式·后端·golang·高并发·简历优化·go面试·后端面经
咖啡啡不加糖6 小时前
雪花算法:分布式ID生成的优雅解决方案
java·分布式·后端
姑苏洛言7 小时前
基于微信公众号小程序的课表管理平台设计与实现
前端·后端
烛阴7 小时前
比UUID更快更小更强大!NanoID唯一ID生成神器全解析
前端·javascript·后端
why1517 小时前
字节golang后端二面
开发语言·后端·golang
还是鼠鼠7 小时前
单元测试-断言&常见注解
java·开发语言·后端·单元测试·maven
cainiao0806057 小时前
Spring Boot 4.0实战:构建高并发电商系统
java·spring boot·后端
Chandler248 小时前
Go 即时通讯系统:日志模块重构,并从main函数开始
后端·重构·golang·gin
酷爱码8 小时前
Spring Boot Starter 自动装配原理全解析:从概念到实践
java·开发语言·spring boot·后端·spring
小奏技术9 小时前
虚拟线程 vs. 传统线程池:Spring Boot 3.x I/O密集型任务性能对比
后端