引言:两大阵营,一场对决
\n\n每位PHP开发者迟早都会面临一个选择:采用函数式风格编写代码,还是深入类与对象的海洋?这就像在瑞士军刀和多功能工具之间做选择——两种工具都能解决问题,但方式不同。在本文中,我们将分析PHP中的函数式与面向对象方法,查看真实代码,并决定何时使用哪种工具。
\n\nPHP是一种独特的语言。它最初是过程式的,后来发展出面向对象特性,而在最新版本(7.4+、8.x)中,它积极吸收了函数式特性:箭头函数、闭包、类型声明。这意味着您可以混合使用不同的编程范式。但如何避免混乱?让我们一起来探讨。
\n\n基础:两个世界的区别
\n\n函数式编程(Functional Programming)
\n\n这里的主角是函数。纯函数没有副作用,始终返回可预测的结果。没有共享状态,没有变量突变。数据像流水线一样按链式传递。
\n\n// 函数式风格:简单、可预测\n$numbers = [1, 2, 3, 4, 5];\n\n$result = array_map(\n fn($n) => $n * 2,\n array_filter($numbers, fn($n) => $n % 2 === 0)\n);\n\nprint_r($result); // [4, 8]\n\n注意到了吗?我们没有修改原始数组,也没有使用循环。一切都是声明式的:取出偶数,加倍,返回。这节省了调试时间——因为没有副作用。
\n\n面向对象编程(OOP)
\n\n面向对象编程围绕对象构建——这些实体存储状态和行为。类封装数据,子类扩展功能,多态性允许动态改变逻辑。
\n\n// 面向对象风格:结构化和可复用\nclass NumberProcessor {\n private array $numbers;\n\n public function __construct(array $numbers) {\n $this->numbers = $numbers;\n }\n\n public function getEvens(): array {\n return array_filter($this->numbers, fn($n) => $n % 2 === 0);\n }\n\n public function multiplyBy(int $factor): self {\n $this->numbers = array_map(fn($n) => $n * $factor, $this->numbers);\n return $this;\n }\n\n public function getResult(): array {\n return $this->numbers;\n }\n}\n\n$processor = new NumberProcessor([1, 2, 3, 4, 5]);\n$result = $processor->getEvens()->multiplyBy(2)->getResult();\nprint_r($result); // [4, 8]\n\n这里我们创建了一个存储状态的对象。可以链式调用方法。优点:如果需要添加新操作,只需在类中增加方法即可。
\n\n何时函数式编程更胜一筹
\n\n1. 无副作用的数处理
\n\n假设您正在编写日志解析器或货币转换器。数据像流水线一样流动:读取 → 过滤 → 转换 → 保存。函数在这里是理想选择。它们不依赖外部状态,易于测试。
\n\n2. 集合与数组
\n\nPHP提供了强大的数组处理函数:array_map、array_filter、array_reduce。结合箭头函数(PHP 7.4+),代码变得如诗般优雅。
$users = [\n ['name' => '安娜', 'age' => 25],\n ['name' => '鲍里斯', 'age' => 17],\n ['name' => '维卡', 'age' => 30],\n];\n\n$adultNames = array_map(\n fn($user) => $user['name'],\n array_filter($users, fn($user) => $user['age'] >= 18)\n);\n\n// ['安娜', '维卡']\n\n简洁、可读,无需类。对于简单的数据转换,这是最佳选择。
\n\n3. 无需模拟(Mock)的测试
\n\n函数没有状态,因此易于测试。无需创建复杂的模拟对象和桩。只需传入数据,即可获得结果。
\n\n何时面向对象编程不可或缺
\n\n1. 带有状态的复杂业务逻辑
\n\n