1. 是什么(What)—— 本质与定义的区别
-
面向对象编程(OOP):
- 本质: 是一种纵向的模块化架构。
- 定义: 它将现实世界中的事物抽象为“对象”,通过**类(Class)**来组织代码。它的核心是将数据(属性)和行为(方法)封装在一起。
- 形象理解: 像是在造零件。如果你要造一辆车,你会定义发动机类、轮胎类。这是一种从上到下的、树状的逻辑结构。
-
面向切面编程(AOP):
- 本质: 是一种横向的模块化架构。
- 定义: 它将那些散落在各个业务模块中的公共功能(如日志、安全、事务、异常处理)抽取出来,形成一个独立的“切面”。
- 形象理解: 像是在加滤镜。不管你是发动机还是轮胎,只要你运行,我都要在你的运行前后“横切”一刀,记录一下运行时间。
2. 为什么(Why)—— 动机与价值的区别
-
为什么需要 OOP?(解决复杂度与重用性)
- 核心痛点: 传统的面向过程代码像面条一样纠缠,难以维护。
- 价值: 通过封装、继承和多态,让代码逻辑清晰、易于重用。它解决了“如何构建业务核心模型”的问题。
- 局限: 当多个不相关的业务模块都需要同一个功能(比如都要记录日志)时,OOP 只能在每个模块里都写一遍,导致代码冗余(代码散射)。
-
为什么需要 AOP?(解决代码解耦与关注点分离)
- 核心痛点: 核心业务逻辑中混杂了太多的非业务逻辑(如权限校验、日志打印),导致主逻辑不清晰。
- 价值: 实现关注点分离(SoC)。让开发者专注于业务逻辑,而将通用的技术支撑功能统一管理。它解决了“如何优雅地处理跨模块的公共逻辑”的问题。
3. 怎么做(How)—— 实现与手段的区别
-
OOP 的手段:封装、继承、多态
- 通过定义
Interface(接口)和Class(类)。 - 通过
extends(继承)来实现父子关系。 - 在 NestJS 中,通过
@Injectable()和constructor注入依赖。
- 通过定义
-
AOP 的手段:代理、拦截、织入
- 核心组件:
- 切点(Pointcut): 定义在“哪里”拦截(比如所有以
find开头的方法)。 - 通知(Advice): 定义“什么时候”干什么(方法执行前、后、还是异常时)。
- 切面(Aspect): 切点 + 通知。
- 切点(Pointcut): 定义在“哪里”拦截(比如所有以
- 在 NestJS 中的落地:
- 你之前问过的 Guards(守卫)、Interceptors(拦截器)、Exception Filters(异常过滤器) 都是 AOP 的具体实现。
- 底层通常使用 Proxy(代理模式),在不修改原代码的情况下,动态地将功能植入。
- 核心组件:
综合对比总结
| 维度 | 面向对象 (OOP) | 面向切面 (AOP) |
|---|---|---|
| 思维维度 | 纵向(业务领域建模) | 横向(技术关注点提取) |
| 核心单位 | 类 (Class) / 对象 (Object) | 切面 (Aspect) |
| 解决的问题 | 把复杂系统拆解为独立的个体 | 把散落在各处的通用功能聚合 |
| 代码关系 | 继承、组合、嵌套 | 拦截、注入、织入 |
| 在 NestJS 中 | 定义 Service、Controller 处理业务 | 定义 Guard 处理权限,Interceptor 记录日志 |
一句话总结: OOP 负责把房子盖好(客厅、卧室、厨房职能分明),而 AOP 负责在所有的房间里安装统一的电路和烟雾报警器(不管你是客厅还是卧室,安全和供电是横向贯穿的)。