December 2, 2025 (4mo ago)

函数式编程 vs 面向对象:快速指南

函数式编程 vs 面向对象:比较范式,查看实用代码示例,并学习如何为你的项目选择最佳方法。

← Back to blog
Cover Image for 函数式编程 vs 面向对象:快速指南

函数式编程 vs 面向对象:比较范式,查看实用代码示例,并学习如何为你的项目选择最佳方法。

函数式编程 vs 面向对象:快速指南

functional programming vs object oriented: Compare paradigms, see practical code examples, and learn how to choose the best approach for your project.

手绘图比较函数式编程与面向对象编程原则并配有示意概念。

介绍

函数式编程与面向对象编程之间的真正差异归结为一个关注点问题:你是围绕“你做什么”来组织代码,还是围绕“谁来做”来组织代码?函数式编程(FP)强调你在做什么,将软件视为无状态、数学函数调用的链条。面向对象编程(OOP)强调谁在做这件事,将逻辑组织在相互交互的有状态对象周围。本指南比较了两种范式,展示了实用的 TypeScript 和 React 示例,并帮助你为项目选择最佳方法。

快速看一下 FP 与 OOP

在函数式和面向对象风格之间做选择不仅仅是一个技术决定,它是一种用于构建软件和处理数据流的思维方式的承诺。这个高层比较为具体示例和实际权衡奠定了基础。

核心哲学差异

OOP 通过将数据和对其操作的函数捆绑到对象中来建模世界。想象一个 User 对象,它保存像姓名和电子邮件这样的数据,并暴露诸如 updateEmail()sendPasswordReset() 之类的方法。每个对象管理它自己的状态。

FP 则采取相反的方法,将数据与行为分离。数据往往是不可变的,纯函数接收输入并返回新的输出而不产生副作用。这减少了在大型 OOP 系统中可能出现的共享状态的纠缠网络。

“以后推理代码才是真正的挑战。FP 将移动部件最小化,而 OOP 将这些部件组织成可理解的组件。”

许多现代团队从两种范式中借鉴思想,以获得两全其美的效果。首先,这里是基本差异的快速总结。

函数式编程与面向对象编程的核心差异

ConceptFunctional Programming (FP)Object-Oriented Programming (OOP)
Primary UnitFunctionsObjects
State ManagementImmutable stateMutable state
Data & OperationsKept separateEncapsulated together
ConcurrencyEasier to handle, statelessRequires explicit locks or synchronization
Flow ControlFunction calls and compositionMethods, loops, and conditionals
Key PrinciplesPure functions, immutabilityEncapsulation, inheritance, polymorphism

理解核心理念

这些范式不仅仅是语法选择;它们是关于数据、行为以及如何构建系统的思维方式。哪个更适合你的项目取决于你希望如何管理变化和复杂性。

概念图示,说明核心理念、数学根基、纯栈、不可变函数、投入对象和多态性。

函数式编程范式

FP 追溯到 λ 演算,将计算视为数学函数的求值。纯函数(对相同输入总是返回相同输出且不产生副作用)是核心。不可变性防止就地更改,鼓励返回新值。

这种可预测性使 FP 代码更容易测试和推理,尤其是在共享可变状态经常导致错误的并发系统中。FP 的单向数据流也倾向于生成更清晰的数据转换流水线。

面向对象编程范式

OOP 将系统建模为封装了状态和行为的交互对象。封装隐藏内部细节,使对象向系统其余部分呈现简单的接口。继承和多态支持代码复用和灵活的抽象。

这种方法非常适合建模复杂的领域实体及其交互。当领域概念稳定且对象关系自然地映射到业务规则时,OOP 可以产生直观且易维护的设计。

可维护性与可扩展性的比较

选择一种范式会影响长期维护和系统的扩展方式。FP 和 OOP 都提供了有价值的策略,但它们处理复杂性的方式不同。

可维护性:可预测性与封装

FP 通过纯函数和不可变性优先考虑可预测性。一个对相同输入始终返回相同结果的函数很容易测试和隔离。OOP 将相关的数据和行为组织在一起,这有助于开发者通过检查自包含的组件来推理系统。

权衡在于:FP 的清晰性来自于将数据与行为分离,而 OOP 的清晰性来自于将它们组合在一起。这会影响调试流程——FP 通常将 bug 缩小到特定函数,而 OOP 可能需要追踪多个对象之间的交互。

可扩展性:并发与并行

FP 的无状态方法简化了并发和并行处理,因为纯函数不会修改共享数据。这减少了对锁和同步的需求。OOP 可以实现并发,但通常需要小心的状态管理和同步机制以避免竞态条件。

FP 在需要可预测并发行为的领域中越来越受欢迎,尽管主流开发者的采用率仍集中在多范式和面向对象语言上 1.2

现实示例:TypeScript 与 React

下面我们用两种风格构建相同的用户设置表单:经典的 React 类组件(OOP)和使用 Hooks 的现代函数组件(FP 风格)。这突出显示了架构选择如何影响状态处理、逻辑重用和结构。

图示说明从基于类的 React 组件向使用 Hooks 的函数式 React 组件的过渡。

OOP 方法:React 类组件

类组件将状态和方法组合到单个对象中,这符合 OOP 的封装模型。

import React, { Component } from 'react';

interface UserSettings {
  name: string;
  email: string;
}

class UserSettingsForm extends Component<{}, UserSettings> {
  state = {
    name: 'Jane Doe',
    email: 'jane.doe@example.com',
  };

  handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = event.target;
    this.setState({ [name]: value } as Pick<UserSettings, keyof UserSettings>);
  };

  handleSubmit = (event: React.FormEvent) => {
    event.preventDefault();
    console.log('Submitting data:', this.state);
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input name="name" value={this.state.name} onChange={this.handleChange} />
        <input name="email" value={this.state.email} onChange={this.handleChange} />
        <button type="submit">Save Settings</button>
      </form>
    );
  }
}

这种模式将相关数据和行为保留在一起,但较大的组件如果不使用高阶组件(higher-order components)等附加模式,可能会变得更难重构和重用。

函数式重构:Hooks 与纯辅助函数

函数式方法将状态与纯函数分离,使各个部分更容易测试和重用。

import React, { useState } from 'react';

const formatUserDataForApi = (name: string, email: string) => ({
  userName: name,
  userEmail: email,
});

const UserSettingsFormFunctional = () => {
  const [name, setName] = useState('Jane Doe');
  const [email, setEmail] = useState('jane.doe@example.com');

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = event.target;
    if (name === 'name') {
      setName(value);
    } else {
      setEmail(value);
    }
  };

  const handleSubmit = (event: React.FormEvent) => {
    event.preventDefault();
    const payload = formatUserDataForApi(name, email);
    console.log('Submitting data:', payload);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" value={name} onChange={handleChange} />
      <input name="email" value={email} onChange={handleChange} />
      <button type="submit">Save Settings</button>
    </form>
  );
};

React 的 Hooks 通过在函数中启用状态和副作用,使这种风格成为主流,并且它们在现代 React 开发中被广泛采纳 3

如何选择正确的范式

这不是一场必须分出输赢的对抗。正确的选择取决于你的团队、问题领域和长期目标。使用以下要点来指导务实的决策。

何时选择 OOP

当你需要建模具有持久状态和行为的丰富领域实体时,选择 OOP,例如:

  • 拥有许多相互关联模块的大型企业系统
  • 组件级状态自然映射到对象的丰富、有状态的 UI 组件
  • 领域具有稳定且定义明确的实体

何时选择 FP

当可预测性、并发和数据转换是优先事项时,选择 FP,例如:

  • 数据处理流水线和 ETL 任务
  • 并发或并行系统中状态同步代价高昂的场景
  • 函数能干净地映射到算法的数学或科学计算

实用决策清单

  1. 你的数据本质如何:是有状态对象还是可变换的流?
  2. 并发对性能和正确性有多关键?
  3. 你的团队具有什么样的专长以及学习新模式的意愿如何?
  4. 是否可以采用混合方法而不强行使用单一范式?

采用混合方法

大多数成功的系统结合了两种范式。像 TypeScript 和 Python 这样的多范式语言允许你在类内部使用不可变数据,对集合应用 mapfilter,并为核心业务逻辑隔离纯函数。

在实践中结合范式

常见的混合模式:

  • 类内部的不可变状态:方法返回新实例而不是修改内部状态。
  • 服务使用纯函数:以无状态函数实现业务逻辑,接受输入并返回输出。
  • 函数式集合方法:使用 mapfilterreduce 处理数组,而不是使用有状态的循环。

用对象来建模“事物”,用纯函数来协调它们之间的行为。这样的分离提高了清晰度和可测试性。

比较面向对象(OP)概念与通过 map filter 流向函数式编程(FP)的流程图。

常见问题

函数式编程比面向对象编程更快吗?

性能取决于问题、语言和运行时。FP 的不可变性可能增加分配开销,但其无状态特性简化了并发,可能在并行系统中提高吞吐量。依赖就地更新的单线程任务可能在 OOP 风格的可变操作下运行得更快。

我可以混合使用函数式和面向对象代码吗?

可以。混合范式很常见,通常也是最实用的选择。用对象建模核心实体,同时将复杂逻辑表达为纯函数。

初学者应该先学哪种范式?

OOP 往往更容易初学,因为类和对象直观地映射到现实世界概念。及早学习核心函数式思想——纯函数和不可变性——可以培养有助于提高代码质量的习惯。

底线建议

  1. 将范式与问题匹配。对于可预测的数据转换和并发使用 FP。对于丰富的领域建模使用 OOP。
  2. 对核心逻辑优先使用小而纯的函数。保持组件和类专注且小型化。
  3. 在有用的地方采用混合方法。你不必对整个代码库只采用一种范式。

实用问答

问:我如何为现有代码库在 FP 与 OOP 之间做决定?

答:评估最大的痛点。如果 bug 来自共享的可变状态,则引入不可变性和纯函数。如果领域天然基于对象,则保留对象但提取无状态服务。

问:如何在 OOP 项目中安全地引入 FP 原则?

答:从小型、可测试的服务开始,以纯函数实现,添加函数式数组方法,并考虑返回新对象实例而不是修改状态。

问:有哪些快速方法可以立即提高可维护性?

答:强制使用小函数,为纯逻辑添加自动化测试,使用 map/filter 代替有副作用的循环,并为有状态对象记录不变式。


1.
https://insights.stackoverflow.com/survey/2023 — Stack Overflow 开发者调查,展示了多范式和脚本语言的主导使用情况,与小众函数式语言相比。
2.
https://octoverse.github.com/ — GitHub Octoverse 报告与语言使用趋势,显示 JavaScript、TypeScript、Python 的广泛采用,而纯函数式语言的份额较小。
3.
https://reactjs.org/docs/hooks-intro.html — React 关于 Hooks 的文档,介绍了 Hooks 如何使函数式组件和可复用的有状态逻辑流行起来。
4.
https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp.html — John Hughes,《为什么函数式编程重要》(Why Functional Programming Matters),一篇关于函数式设计益处的基础性文章。
← Back to blog
🙋🏻‍♂️

AI编写代码。
您让它持久。

在AI加速的时代,干净代码不仅仅是好的实践 — 它是能够扩展的系统与在自己的重量下崩溃的代码库之间的区别。