面向过程(Procedural)
思路:按步骤组织代码。把复杂任务拆成一系列函数,依次调用。
核心关注:做什么(步骤)。
读取数据 → 处理数据 → 输出结果适合:简单、线性的任务;脚本;性能敏感的场景。
C 语言是典型的面向过程语言(虽然也能实现类似对象的结构)。
面向对象(OOP)
思路:把程序看作对象的集合。每个对象有自己的数据(属性)和行为(方法)。
核心关注:谁来做(对象)。
四大特性:
一、封装(Encapsulation)
把数据和操作数据的方法打包在一起,并控制外部访问的权限。
目的:隐藏实现细节,只暴露必要的接口。
二、继承(Inheritance)
子类继承父类的属性和方法,可以复用和扩展。
目的:代码复用。
风险:过深的继承层次会导致代码难以理解和维护("脆弱基类问题")。
三、多态(Polymorphism)
同一个接口,不同的实现。
目的:调用方不需要知道具体类型,只依赖抽象接口——降低耦合。
四、抽象(Abstraction)
提取本质特征,忽略细节。
对比
| 面向过程 | 面向对象 | |
|---|---|---|
| 组织单位 | 函数/步骤 | 对象/类 |
| 数据与行为 | 分离 | 绑定在一起 |
| 适合 | 简单、线性任务 | 复杂、状态多的系统 |
| 复用方式 | 函数调用 | 继承、组合、多态 |
一个重要的现代观点
"组合优于继承"(Composition over Inheritance)
GoF 设计模式中提出的原则:优先使用组合(把对象作为另一个对象的成员),而非继承。
为什么:
- 继承是一种强耦合关系——子类依赖父类的实现细节;
- 继承层次一旦设计错误,修改成本很高;
- 组合更灵活,可以在运行时改变行为(如策略模式)。
继承适合"is-a"关系(猫是一种动物),组合适合"has-a"关系(车有引擎)。
误用继承是 OOP 中最常见的问题之一——用继承来复用代码,而不是表达真实的分类关系。
现代语言的态度
纯 OOP 语言(如 Java)要求一切都是类——这在某些情况下显得笨重(如只有静态方法的工具类)。
现代趋势是多范式融合:
- Python/JavaScript/Rust/Go:支持多种范式,不强制 OOP;
- 函数式编程的影响在增强(不可变数据、纯函数、高阶函数);
- Go 语言干脆没有类和继承,用组合和接口实现多态。
什么时候用什么
简单的脚本、数据处理流程:用面向过程(甚至函数式风格)更直接。
复杂的、状态多、需要长期维护的业务系统:对象提供了有效的组织方式——把相关的状态和行为放在一起,符合人类的认知习惯。
关键:不要教条。 一个系统里可以同时使用多种范式——用对象组织领域模型,用函数处理数据转换,这很常见。
一个实用判断
如果你发现一个类里只有方法、没有状态(或只有静态方法),那它可能是"伪装成类的函数"。
反之,如果你发现一组数据总是被同样的几个函数一起操作——那它们可能应该封装成一个对象。
说明:本文为编程范式的知识整理。