探讨编程的本质
# 一、引子:一个让人深思的问题
前段时间面试,一位资深技术总监抛出了一个看似简单的问题:"说说你对软件工程的理解。"
说实话,这个问题让我当场愣住了。写了这么多年代码,突然被问到这么本质的问题,所有的技术细节在那一刻都显得苍白无力。这就像一个每天开车的老司机,突然被问"什么是驾驶"一样。
# 二、从编程开始:寻找答案的起点
这个问题在脑海里挥之不去。软件工程太宏大了,它涵盖了计算机科学、系统设计、项目管理等诸多领域。作为一个写代码的程序员,我决定从最熟悉的地方入手——编程。
毕竟,大部分时间我都在写业务代码,偶尔涉及些中间件和架构设计。如果能搞清楚编程的本质,也许就能窥见软件工程的一角。
# 三、编程的本质:逻辑与控制的分离
在寻找答案的过程中,我读到了陈皓(左耳朵耗子)在专栏里的一个观点,让我豁然开朗:
编程 = 算法 + 数据
算法 = 逻辑 + 控制
这个公式看起来简单,但背后蕴含着深刻的软件设计思想。用图来理解会更直观:
# 1、什么是逻辑?什么是控制?
- 逻辑:描述"做什么"(What),是业务规则和数据的声明
- 控制:描述"怎么做"(How),是执行流程和实现细节
举个实际的例子,假设我们要验证一个用户注册表单:
// 逻辑部分:声明验证规则
const userRegistrationRules = {
form_id: 'create_user',
fields: [
{ id: 'name', type: 'text', min_length: 3, required: true },
{ id: 'password', type: 'password', min_length: 8, pattern: /^(?=.*[A-Za-z])(?=.*\d)/ },
{ id: 'repeat-password', type: 'password', must_match: 'password' },
{ id: 'email', type: 'email', unique: true }
]
};
// 控制部分:执行验证
const result = validateForm(userRegistrationRules);
看到区别了吗?userRegistrationRules
声明了验证规则,这是纯粹的业务逻辑,产品经理都能看懂。而validateForm
函数负责如何执行这些规则,这是技术实现细节。
这种分离带来了巨大的好处:
- 业务规则变化时,只需修改逻辑部分
- 实现方式优化时,只需调整控制部分
- 两者可以独立测试和演进
# 四、从微观到宏观:架构层面的思考
理解了编程的本质,我开始思考:这种"逻辑与控制分离"的思想能否应用到架构设计上?
# 1、现实的困境
最近在做一个企业级CRM系统,随着业务发展,系统变得越来越复杂:
- 多租户隔离
- 多条业务线并行
- 业务线之间相互依赖
- 个性化需求层出不穷
结果呢?代码像一团乱麻,老员工疲于救火,新员工无从下手。明明用了SOLID
原则,设计模式也没少用,为什么还是这样?
# 2、领域驱动设计的启发
问题出在哪?我意识到,光有技术手段是不够的,缺少的是对业务的深入理解和合理建模。
这时我接触到了领域驱动设计(DDD),它带来了两个重要启示:
- 统一语言:开发者、产品经理、业务专家用同一套语言交流,这套语言直接体现在代码中
- 领域建模:不是从技术角度,而是从业务角度对系统进行建模
举个例子,在CRM系统中:
// 传统做法:技术视角
class CustomerService {
void updateCustomerData(Map<String, Object> data) {
// 各种if-else处理不同字段
}
}
// DDD做法:业务视角
class Customer {
void upgrade(MembershipLevel newLevel) {
// 升级会员等级的业务逻辑
}
void assignToSalesman(Salesman salesman) {
// 分配销售的业务规则
}
}
看出区别了吗?DDD让代码直接表达业务意图,而不是技术实现。
# 3、实践中的探索
理论很美好,但怎么落地?我在GitHub上找到了阿里开源的COLA框架(原COPA已更新),它提供了一套完整的DDD实践方案。框架分为几个层次:
- 应用层(Application):处理用户请求,编排业务流程
- 领域层(Domain):核心业务逻辑,领域模型
- 基础设施层(Infrastructure):技术支撑,数据访问
这不正是"逻辑与控制"思想的体现吗?领域层是纯粹的业务逻辑,应用层负责流程控制,基础设施层处理技术细节。
# 五、融会贯通:本质的统一
经过这番探索,我发现从微观的代码设计到宏观的架构设计,"逻辑与控制分离"这个思想贯穿始终。
# 1、设计模式中的体现
以策略模式为例,看看它如何体现这种思想:
// 逻辑:不同的优惠策略
interface DiscountStrategy {
BigDecimal calculate(BigDecimal originalPrice);
}
class PercentageDiscount implements DiscountStrategy {
// 9折优惠
public BigDecimal calculate(BigDecimal originalPrice) {
return originalPrice.multiply(new BigDecimal("0.9"));
}
}
class ThresholdDiscount implements DiscountStrategy {
// 满100减20
public BigDecimal calculate(BigDecimal originalPrice) {
if (originalPrice.compareTo(new BigDecimal("100")) >= 0) {
return originalPrice.subtract(new BigDecimal("20"));
}
return originalPrice;
}
}
// 控制:选择和执行策略
class PriceCalculator {
private DiscountStrategy strategy;
public BigDecimal calculateFinalPrice(Order order) {
// 根据条件选择策略(控制逻辑)
if (order.isVipCustomer()) {
strategy = new PercentageDiscount();
} else if (order.getTotalAmount().compareTo(new BigDecimal("100")) > 0) {
strategy = new ThresholdDiscount();
}
// 执行策略(调用逻辑)
return strategy.calculate(order.getTotalAmount());
}
}
# 2、架构层面的映射
再看DDD架构,同样的思想体现在不同层次:
- 领域层:纯粹的业务逻辑,就像策略模式中的各种策略实现
- 应用层:流程控制,就像策略模式中的Context
- 基础设施层:数据和技术支撑
这种分层不是为了分层而分层,而是为了实现关注点分离:
- 业务专家关注领域层
- 架构师关注应用层的编排
- 技术专家关注基础设施层的实现
# 六、回到最初的问题
现在,如果再被问到"你对软件工程的理解",我会这样回答:
软件工程的本质是管理复杂度。而管理复杂度的核心方法是分离关注点——把"做什么"和"怎么做"分开,把业务逻辑和技术实现分开,把不同的责任分配给不同的模块。
从一行代码到整个系统架构,这个原则始终适用。理解了这一点,无论是写代码还是做架构,都有了一个清晰的指导原则。
编程不只是敲代码,它是一种思维方式,一种把复杂问题简单化的艺术。而这,或许就是软件工程的魅力所在。