buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

编程的本质:LCD-逻辑、控制与数据的解耦艺术

本文基于《左耳听风:传奇程序员练级攻略》的读书笔记整理而成

image

编程的本质可以归纳为三个核心要素:逻辑(Logic)控制(Control)数据(Data)。我们所使用的各种编程范式和设计方法,本质上都是围绕这三个方面展开工作。

  • 逻辑:反映问题的本质,定义如何解决问题的具体方法
  • 控制:解决问题的策略,如循环执行、异步执行等流程控制
  • 数据:问题的表现形式和载体

在实际开发中,业务逻辑本身往往就很复杂,这是程序复杂度的主要来源之一。当这些复杂的逻辑与控制流程交织在一起时,就构成了程序的完整复杂度。

因此,有效分离逻辑、控制和数据,成为编写高质量程序的关键。这涉及到软件工程中的一个核心概念——解耦:通过降低模块间的依赖程度,消除不必要的耦合关系。

从聚合支付案例看LCD的演变

让我们通过一个聚合支付系统的例子,来理解逻辑、控制和数据的分离过程。

初始版本:纯粹的业务逻辑

聚合支付系统收到支付请求,保存支付数据,然后请求外部银行接口。

我们简化为下面的Logic-逻辑:

pay(PayRequest request) {
    PayData data = from(request);
    savePayData(data);
    invokeBankAPI(data);
    return "支付受理成功";
}

这个版本只关注核心业务逻辑:转换数据、保存记录、调用银行接口。

引入控制:异步执行(为便于阅读和讲解,这里使用Thread)

pay(PayRequest request) {
    PayData data = from(request);
    savePayData(data);
    
    // 控制:异步执行
    new Thread(() -> {
        invokeBankAPI(data);
    }).start();
    
    return "支付受理成功";
}

这里引入了控制元素——异步执行。我们只需启动一个线程,Java运行时环境会处理具体的异步执行细节。

增加事务控制,以及创建时间、创建人等审计字段赋值

@Transactional
pay(PayRequest request) {
    PayData data = from(request);
    data.setCreateBy(***);    // 数据加工
    data.setCreateTime(***);  // 数据加工
    savePayData(data);
    
    new Thread(() -> {
        invokeBankAPI(data);
    }).start();
    
    return "支付受理成功";
}

增加了事务控制和数据加工逻辑,代码开始变得复杂。

添加幂等控制

@Transactional
pay(PayRequest request) {
    key = createKey(request);
    if(!redis.setnx(key)){
        return "请勿重复发起";
    }

    PayData data = from(request);
    data.setCreateBy(***);
    data.setCreateTime(***);
    savePayData(data);
    
    new Thread(() -> {
        invokeBankAPI(data);
    }).start();
    
    return "支付受理成功";
}

幂等控制的加入进一步增加了代码复杂度。

异常处理,并要区分异常类型做不同处理

@Transactional
pay(PayRequest request) {
    try {
        key = createKey(request);
        if(!redis.setnx(key)){
            return "请勿重复发起";
        }

        PayData data = from(request);
        data.setCreateBy(***);
        data.setCreateTime(***);
        savePayData(data);
        
        new Thread(() -> {
            invokeBankAPI(data);
        }).start();
        
        return "支付受理成功";
    } catch(Exception e) {
        log.error("程序异常", e);
        if (e instanceof ChannelException) {
            // 特殊处理
        }
    }
}

至此,这段代码已经比较复杂了,包含了多种关注点:业务逻辑、异步控制、事务管理、幂等校验和异常处理。

解耦与重构:回归LCD本质

当代码变得如此复杂时,我们需要重新思考设计,将逻辑、控制和数据进行有效分离:

  1. 封装幂等组件:将幂等校验逻辑抽象为独立组件
  2. 实现全局异常处理:通过统一的异常处理机制避免业务代码中的try-catch块
  3. 参数校验器:独立处理参数验证逻辑
  4. 数据转换组件:专门负责数据转换和加工
  5. 全局审计字段处理:通过AOP或框架机制自动设置创建人、创建时间等字段
  6. 控制逻辑迁移:将异步调用银行接口的代码迁移到专门的银行通道实现类中

通过这种解耦方式,我们可以让核心业务逻辑保持简洁和清晰,而将各种控制 concerns 和数据处理逻辑放到适当的位置和层次中。

结语

编程的本质在于合理组织逻辑、控制和数据三者之间的关系。优秀的程序设计不是避免复杂性,而是通过有效的分离和解耦来管理复杂性。当我们能够清晰区分"做什么"(逻辑)、"怎么做"(控制)和"用什么做"(数据)时,代码的可读性、可维护性和可扩展性都将得到显著提升。

这种LCD分离思想不仅适用于单个方法或类,也同样适用于系统架构的设计。在任何层级上,保持逻辑、控制和数据的清晰边界,都是高质量软件的关键特征。

posted on 2024-09-23 20:19  buguge  阅读(164)  评论(0)    收藏  举报