PHP代码重构:何时需要以及如何不损害项目
\n\n每位PHP开发者迟早都会遇到想要重写的代码。遗留PHP项目充满了“魔法数字”(magic numbers)、长达一屏的函数以及负责所有事情的类。你很想动手整理一番。但值得吗?如果值得——如何安全有效地进行PHP代码重构?
\n\n在本文中,我们将分析何时确实需要重构,何时最好保持原样。你将看到PHP中糟糕代码和优秀代码的真实示例,获得一份操作检查清单,并了解如何避免将代码改进变成一场灾难。
\n\n何时PHP代码重构是合理的?
\n\n重构是在不改变代码外部行为的情况下改变其内部结构。听起来简单,但在实践中这是开发中最危险的活动之一。以下是需要考虑改进PHP代码的迹象:
\n\n- \n
- 代码有“坏味道”(code smells)。 长方法、重复代码、上帝类(God objects)、难以理解的变量名。 \n
- 添加新功能变得痛苦。 如果每个拉取请求都会破坏其他三个地方——这是一个警示信号。 \n
- 你在一周后就不理解自己写的方法做了什么。 糟糕的代码是需要为每一行写注释的代码。 \n
- 违反了单一职责原则(SRP)。 一个既创建用户、又发送邮件、还生成报告的UserManager类——是重构的典型候选。 \n
但有些情况下,PHP代码重构可能会造成损害:
\n- \n
- 明天就是截止日期,而代码还能运行。 \n
- 没有测试。没有测试,任何更改都像在玩俄罗斯轮盘赌。 \n
- 代码是临时为原型编写的,而原型明天就会被丢弃。 \n
安全重构的规则
\n\n在着手进行PHP代码重构之前,请记住三条铁律:
\n\n- \n
- 用测试覆盖代码。 至少是集成测试。如果没有测试——为关键部分编写测试。 \n
- 小步重构。 一次只重构一个方法。推送——检查——继续前进。 \n
- 不要混合重构和添加功能。 这是两个不同的任务。在不同的提交中完成它们。 \n
PHP代码重构示例:从糟糕到优秀
\n\n让我们看一些真实示例。假设我们有一个计算折扣的函数。在典型的遗留项目中,它看起来像这样:
\n\n重构前(糟糕的代码)
\nfunction calculateDiscount($user, $order) {\n $discount = 0;\n if ($user->isVip()) {\n $discount = 10;\n if ($order->getTotal() > 1000) {\n $discount = 15;\n }\n }\n if (date('Y-m-d') == '2025-12-31') {\n $discount = $discount + 5;\n }\n if ($order->getItemsCount() > 5) {\n $discount = $discount + 3;\n }\n return $discount;\n}\n\n问题:魔法日期、数字常量、逻辑混合(用户、订单、日期)。如果明天出现新的促销活动——这个函数会变得更长。
\n\nPHP重构后(优秀的代码)
\nclass DiscountCalculator {\n private const NEW_YEAR_DATE = '2025-12-31';\n \n public function calculate(User $user, Order $order): int {\n $discount = $this->getBaseDiscount($user) \n + $this->getHolidayBonus() \n + $this->getVolumeBonus($order);\n \n return min($discount, 50); // 不超过50%\n }\n \n private function getBaseDiscount(User $user): int {\n if ($user->isVip() && $user->getTotalSpent() > 1000) {\n return 15;\n }\n return $user->isVip() ? 10 : 0;\n }\n \n private function getHolidayBonus(): int {\n retu