编程范式

范式是你戴上的眼镜——戴上之前,你不知道自己在用什么方式看世界。

编程语言是工具,范式是工具背后的世界观。Python、TypeScript、Rust 都同时支持多种范式,区别不在于能不能用,而在于倾向和表达方式。

范式的核心问题只有一个:

程序是什么?

六个范式,给出了六个不同的答案。

六个答案

范式程序是什么基本单元状态观组合方式
面向对象相互通信的对象社会对象(数据 + 行为)可变,封装保护消息传递 / 组合
函数式纯变换的复合函数(输入 → 输出)不可变,无副作用函数组合
过程式时序指令的集合语句 / 过程可变,显式操控顺序、分支、循环
声明式意图的规格说明规则 / 约束隐含,运行时管理组合约束与关系
泛型类型无关的通用抽象参数化算法 / 类型正交(不关心)类型实例化
响应式随时间传播的数据流事件 / 信号 / 流响应式,自动传播流操作符 / 依赖图

没有哪个答案是错的。每个答案都是对计算本质的一种有效抽象——只是选择了不同的切面。

一张关系图

六个范式不是并列的知识点,它们之间有深刻的结构关系:

flowchart TB
    Q(["程序是什么?"])

    subgraph 控制["控制反转光谱"]
        P03a["过程式\n程序员控制时序"]
        P03b["声明式\n运行时控制执行"]
        P03a -->|"控制权让渡"| P03b
    end

    subgraph 状态["状态管理对立"]
        P01["面向对象\n封装可变状态"]
        P02["函数式\n消灭可变状态"]
        P01 <-->|"对立互补"| P02
    end

    P05["响应式\n时间作为数据"]
    P04(["泛型\n正交横切面"])
    P06["设计模式\n范式的词汇表"]

    Q --> 控制
    Q --> 状态
    Q --> P05
    P02 -->|"时间维度延伸"| P05
    P04 -. 横切所有范式 .-> 控制
    P04 -. 横切所有范式 .-> 状态
    P06 -->|"沉淀词汇"| 状态
    P06 -->|"沉淀词汇"| 控制

贯穿主线:时间观的谱系

读完这六篇,会发现所有范式都在以不同方式回答同一个问题:程序如何对待时间?

这是整个系列最核心的一条隐藏主线:

范式对时间的态度手段
过程式时间是我的领地,我控制每一步显式步骤序列,副作用时序由程序员保证
面向对象时间被局部化在对象边界内可变状态封装在对象里,对象是变化的责任人
函数式时间是问题,消灭它不可变性——每个”变化”都是新值的产生
声明式时间是运行时的事,不是我的事委托——描述终态,执行时序外包给运行时
泛型与时间无关(正交)横切所有范式,不改变状态观和时态观
响应式时间是数据,是可操作的材料流和信号——把时间维度上的值变成一等公民

怎么读这张表:从过程式到响应式,是程序员对时间控制权的逐步让渡。过程式握着全部控制权;OOP 把控制权交给对象;FP 假装时间不存在;声明式把时序外包给运行时;响应式把时间本身变成数据,让外部事件决定触发时机。一门语言选择哪种时间观,决定了它在哪类问题上最自然。

隐藏的交叉结构

单读任何一篇都看不到的联系,在这里点出。

状态管理的三角张力

OOP、FP、响应式三者构成一个张力三角,都在回答”可变状态应该如何存在”:

OOP:封装可变状态
    把状态锁在对象边界内,指定对象为责任人
    但并发时封装失效,共享可变状态仍是问题
 
FP:消灭可变状态
    每个变化是新值,不修改旧值
    纯粹但有代价:现实世界的 I/O 必须被隔离
 
响应式:约束状态传播
    承认状态会变化,但把变化的传播显式建模为流
    时间不是问题,而是可以操作的材料

三种答案不是对立的,是互补的。Functional Core / Imperative Shell 是 FP + OOP 的和解;Redux 是 FP 不可变性 + 响应式单向数据流的结合;React Server Components 是声明式 UI + FP 纯函数 + OOP 组件模型的融合。

控制反转的完整光谱

从”谁控制执行”这个角度,六个范式排成一条连续的光谱:

过程式          OOP             声明式          响应式
│               │               │               │
程序员控制      对象控制自己     运行时控制       外部事件控制
每一步时序      的状态变化       执行策略         触发时机
│               │               │               │
最大控制权                                       最小控制权
最高认知负担                                     最低认知负担

泛型横切这条光谱——它和状态观、控制观无关,只关心类型层面的抽象。Vec<T> 可以是过程式的,Observable<T> 可以是响应式的,泛型让两者都能安全地参数化。

词汇如何演化为语言

设计模式是范式的词汇表,但词汇有生命周期:

范式产生问题 → 程序员发现解法 → 解法被命名为模式 → 模式被广泛采用 → 下一代语言内建为特性 → 模式消失

已经完成这个演化的例子:

  • Iterator 模式for...of / .iter()(所有现代语言内建)
  • Strategy 模式 → 高阶函数(FP 语言中完全透明)
  • Observer 模式 → EventEmitter → Signal(正在成为语言级原语)
  • Async Monadasync/await(所有现代语言内建)

模式密度是语言表达力的反指标:一门语言需要手动实现的模式越多,说明它在那个维度上的表达力越弱。Rust 内建了 trait + enum + match,消解了大量行为型模式;Python 内建了一等函数 + 模块系统,消解了策略和单例。

泛型是正交横切面

泛型不属于任何一个范式,却可以和所有范式组合:

泛型 + 过程式  → 参数化算法(sort<T: Ord>)
泛型 + FP      → Functor、Monad、高阶类型
泛型 + OOP     → 泛型容器(Vec<T>、HashMap<K,V>)
泛型 + 响应式  → Observable<T>、Signal<T>

这种正交性使泛型成为标准库的骨架——所有语言的标准库几乎都建立在泛型之上,因为容器和算法天然不依赖于具体类型。

文章索引

基础分野

主流范式

扩展维度

导读建议

路径一:从对立到融合(推荐初次阅读)

03 → 01 → 02 → 05 → 04 → 06

先理解最基础的分野(过程式与声明式),再深入最主流的两对立范式(OOP 与 FP),然后看时间维度的延伸(响应式),再看正交横切面(泛型),最后用设计模式做总结。

路径二:按时间观的谱系

01 → 02 → 03 → 05 → 04 → 06

按”时间观的谱系”表格的顺序,体会从过程式到响应式,控制权如何逐步让渡。

路径三:按工程关切跳读

  • 关心状态管理与并发 → 01 面向对象 + 02 函数式
  • 关心前端架构与数据流 → 05 响应式 + 03 声明式
  • 关心类型安全与抽象 → 04 泛型
  • 关心团队协作与设计决策 → 06 设计模式

读完之后,面对一段代码,第一反应不再是”这用了哪个语法”,而是”这段代码的作者用了哪种世界观看待程序,他对状态、时间、控制权做了什么选择,代价是什么”。


跨区链接