Skip to main content

范式

📅 2026-03-27 ✏️ 2026-03-27 CS
No related notes

1 · 范式

S 程序本质上就是在操作状态(数据),但随着规模增长,状态管理越来越难 C 不同范式对”状态该由谁持有、能否修改、怎么传播”给出了截然不同的回答 Q 各范式如何表示状态、改变状态、控制状态?各自的取舍是什么? A 见下文

1.1 · 核心视角

从状态看编程范式:怎么表示状态、怎么改变状态、谁来控制状态

1.2 · 范式总览

  • 面向过程(Procedural)—— 不隔离状态

状态存在于”变量”和”过程执行过程”里,由过程直接读写。

  • 面向对象(OOP)—— 隔离状态在对象内部

状态存在于”对象内部”,通过方法(对象行为)来控制访问。

  • 函数式(FP)—— 避免改变状态

尽量不原地改状态,而是返回新状态

  • 响应式(Reactive)—— 状态变化自动传播

状态表示为”随时间变化的值流”(Observable/Signal),变化沿声明好的依赖图自动传播,消费者订阅而非主动轮询。

  • Actor 模型 —— 状态隔离在 Actor 内部,通过消息传递

每个 Actor 拥有私有状态,外部完全不可见;Actor 之间只能通过异步消息通信,收到消息后可以修改自身状态、创建新 Actor、发送新消息。没有共享内存,从根源消除锁竞争。

1.3 · 深入对比

维度面向过程OOPFPReactiveActor
状态位置全局/局部变量对象字段函数参数与返回值流/SignalActor 私有
可变性随意可变受方法约束不可变(或显式化)流式推导,源头可变Actor 内可变,外不可见
状态变化方式直接赋值方法调用产生新值事件/信号驱动消息驱动
并发策略锁/监视器天然安全(无共享可变)背压/调度器天然安全(无共享内存)

OOP 深入:

  • 封装:将数据和操作绑定,对外暴露接口,隐藏内部状态表示 → 通过封装”变化”来让程序更容易理解
  • 多态:允许不同对象对同一消息做出不同响应,核心价值是让调用者不必了解具体状态的形态
  • 继承 vs 组合:继承共享状态结构,但容易产生”脆弱基类”问题;组合更灵活,优先使用
  • 陷阱:对象之间通过引用互相持有,容易形成隐式的共享可变状态,并发下尤其危险

FP 深入:

  • 纯函数:相同输入永远得到相同输出,无副作用 → 通过减少”变化”来让程序更容易理解
  • 不可变数据:数据一旦创建不可修改,“修改”即创建新副本(持久化数据结构可降低拷贝开销)
  • 显式化副作用:用类型系统把副作用标注出来(IO Monad、Effect 系统),让”哪里有状态变化”在类型层面可见
  • 组合性:纯函数天然可组合、可测试、可缓存(memoization)

Reactive 深入:

  • 核心抽象:Observable(RxJava/RxJS)或 Signal(SolidJS/Angular),将”值随时间变化”建模为一等公民
  • 声明式依赖c = a + b,当 a 或 b 变化时 c 自动更新,无需手动通知
  • 背压(Backpressure):当生产者快于消费者时,提供缓冲、丢弃、限流等策略
  • 适用场景:UI 状态管理、事件流处理、实时数据管道

Actor 深入:

  • 代表实现:Erlang/OTP(进程即 Actor)、Akka(JVM)
  • 容错:Supervisor 树,子 Actor 崩溃后由父 Actor 决定重启策略(“let it crash”哲学)
  • 位置透明:消息发送不区分本地/远程,天然适合分布式系统
  • 适用场景:高并发、分布式、需要强隔离和容错的系统

1.4 · 一句话总结

面向过程 → 状态裸奔;OOP → 状态封装;FP → 状态不变;Reactive → 状态流动;Actor → 状态隔离+消息;

没有最好的范式,只有最适合问题域的范式。现代语言(Rust、Scala、Kotlin)往往混合多种范式。

2 · links#

  1. https://www.johndcook.com/blog/2010/11/03/object-oriented-vs-functional-programming/