防止、检测和修复代码中的错误的 7 个技巧

发布日期 作者

7 Tips to Prevent, Detect, and Fix Bugs In Your Code image

我们为项目添加的每一行代码都会增加项目的复杂性,也增加了引入错误的可能性,这些错误可能会在不适宜的时候出现。也许是在客户会议前几分钟,或者是在周末我们去电影院的时候,远离我们的键盘。

为了防止这些可怕的情况,让我们回顾一下编写更好代码的七个技巧

  1. 为您的变量、函数、参数和方法分配描述性名称

代码只编写一次,但会被读取和解释很多次——无论是其他开发人员还是您自己。因此,花几秒钟来命名新的类或方法是值得的,这样它的名称就能揭示其真正的意图或内容。

让我们比较这两行代码。哪一行更容易理解?

$evnt->add($req->q);
$event->addTickets($request->quantity);

第一行代码有一个拼写错误,add 方法不清楚添加了什么内容,变量 $req 不够清晰,而且很难理解 q 代表数量。

另一方面,第二个示例即使对于非开发人员来说也很容易理解。

  1. 使用 PSR-2 等标准来编写 PHP 代码

永远不要低估以有序和一致的方式编写代码的重要性,因为这将使您能够更快地发现问题。

考虑以下两个示例

public function addTickets($quantity)
{
foreach (range(1, $quantity) as $i)
{
$code = Code::generate(); }
$this->tickets()->create(
[
'code' => $code,
]);
}
public function addTickets($quantity)
{
foreach (range(1, $quantity) as $i) {
$code = Code::generate();
}
 
$this->tickets()->create([
'code' => $code,
]);
}

两段代码都有相同的错误:它们只创建了一张票,而应该创建 N 张票。但是您在哪一段代码中更快地发现了问题?现在想象一下,如果处理的是格式错误的复杂代码,会造成什么后果。

  1. 减少临时变量的数量

虽然我们在算法中学习到的第一个概念之一是如何声明和使用临时变量,但它们会使代码更难阅读和维护

考虑以下示例

$contact = array();
$contact['firstname'] = $user->first_name;
$contact['surname'] = $user->last_name;
$contact['id'] = $user->id;
$contact_emails = array();
$contact_email = array();
$contact_email['email'] = $user->email;
$contact_emails[] = $contact_email;
$contact['emails'] = $contact_emails;
 
$this->create('contact', $contact);
$contact = [
'id' => $user->id,
'firstname' => $user->first_name,
'surname' => $user->last_name,
'emails' => [
[
'email' => $user->email,
],
],
];
 
$this->create('contact', $contact);

哪个示例更容易理解?

顺便说一下,将等号对齐是不好的做法。这不仅违反了 PSR-2,还会使代码更难维护。

因此,回到我们的票务示例,如果我们消除 code 变量并将代码内联,则可以改进示例

public function addTickets($quantity)
{
foreach (range(1, $quantity) as $i) {
$this->tickets()->create([
'code' => Code::generate(6),
]);
}
}

但是,在某些情况下,使用局部变量可以提高代码的清晰度,例如

function calculateCode($price, $quantity, $deliveryCost)
{
$subtotal = $price * $quantity;
 
if ($subtotal < 30) {
$subtotal += $deliveryCost;
}
 
return $subtotal;
}

可能比以下代码更清晰

<?php
 
function calculateTotal($price, $quantity, $deliveryCost)
{
if ($price * $quantity < 30) {
return $price * $quantity + $deliveryCost;
}
 
return $price * $quantity;
}
  1. 不要使用“魔数”。

如果低于 30 美元的订单将收取送货费,我们应该使用属性、常量或配置变量来揭示此信息,如下所示

if ($subtotal < DELIVERY_COST_THRESHOLD) {
$subtotal += $deliveryCost;
}

这样,我们就可以揭示我们的意图,并且还可以将该常量在项目的其他部分重复使用。

如果我们以后需要更改送货阈值,我们只需要更新代码中的一行,通过减少重复,我们也减少了忘记更新之前使用魔数的地方的可能性。

  1. 分而治之

许多示例和代码场景可以通过将代码分离成多个小方法来改进,每个方法都有不同的职责。例如

新方法 getContactInfo 将返回一个包含用户联系信息的数组

$this->create('contact', $user->getContactInfo());

面向对象编程要求我们在一个地方(类)组合数据和函数。我们将在包含所有用户信息(User 模型)的地方组装包含联系信息的数组。

让我们看另一个示例

$subtotal = $item->price * $quantity;
$subtotal = $this->addDeliveryCost($subtotal);

方法 addDeliveryCost 将返回包含送货费的金额,但前提是金额不超过送货阈值,否则将返回原始金额。

现在让我们删除局部变量并将代码内联

return $this->addDeliveryCost($price * $quantity);

声明和使用许多小方法是减少代码中临时变量需求的好方法。

  1. 默认使用简单解决方案

许多承诺帮助您编写更好代码的教程最终解释了如何过度复杂化本可以保持简单的代码。

这些教程告诉您,如果您正在使用 Laravel 和 Eloquent,将以下代码放在控制器中是错误的

// Somewhere in UserController.php
 
User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
]);

您应该改为以下方式编写

// Somewhere in UserController.php
 
$this->commandTransport->handleCommand(
new UserCreationCommand(
new UserNameField($request->name),
new UserEmailField($request->email),
new UserPasswordField(bcrypt($request->password)),
)
);

然后,在 UserCreationCommandHandler 类中,您也不会创建用户,因为这会违反 SOLID 原则。您应该改为使用存储库

class UserCreationCommandHandler
{
//...
 
public function handle(UserCreationCommand $command)
{
$this->userRepository->create(
$command->name,
$command->email,
$command->password,
);
}
}

最终,在 UserEloquentRepository 中,您将最终调用 User::create

class UserEloquentRepository implements UserRepository
{
//...
 
public function create(
UserNameField $name,
UserEmailField $email,
UserPasswordField $password
) {
return User::create([
'name' => $name->getValue(),
'email' => $email->getValue(),
'password' => bcrypt($password->getValue()),
]);
}
 
}

经过半小时的工作后,客户打电话给您,要求您在 User 模型中添加另一个字段。

哪个示例需要更长时间才能修复?在什么情况下更容易引入错误?(例如,忘记将一个字段从一个方法传递到另一个方法)。

顺便说一下,您是否注意到我在命令和存储库的示例中两次调用了 bcrypt?因此第二个示例中有一个错误!

不幸的是,几十个接口和类并不能阻止您犯错误。在任何情况下,您都必须非常仔细地测试您的代码。说到测试代码

  1. 编写自动测试

会计人员遵循一种称为“复式记账”的做法——该方法要求他们对所有交易进行两次录入。编写单元测试要求我们编写两次代码,一次是为了定义每个测试

function test_order_without_delivery_cost()
{
$order = new Order;
$order->addItem(new Item(['price' => 20]), 5);
 
$expectedTotal = 20 * 5;
$this->assertSame($expectedTotal, $order->getTotal());
}
 
function test_order_with_delivery_cost()
{
$order = new Order;
$order->addItem(new Item(['price' => 20]), 1);
 
$expectedTotal = 20 + DELIVERY_COST;
$this->assertSame($expectedTotal, $order->getTotal());
}

以及第二次为了编写代码的实际实现(我将其留作读者的练习)。

许多开发人员抱怨这种做法,因为它迫使我们“加倍工作”,但通过编写两次代码,我们减少了以相同方式两次犯同样错误的可能性(如果我们犯了两个不同的错误,测试很可能会失败)。这就是为什么实现了一些单元测试的项目往往具有更少的错误,并且需要更少的调试时间。

Duilio Palacios photo

我是一名 PHP/Laravel 开发人员和讲师。我创建了 Styde.net,这是一个专门向西班牙社区教授 PHP、Laravel、Vue.js 和其他 Web 技术的网站。

归档于
Cube

Laravel 时事通讯

加入 40000 多名其他开发者,绝不错过任何新的技巧、教程等信息。

Laravel Forge logo

Laravel Forge

轻松创建和管理您的服务器,并在几秒钟内部署您的 Laravel 应用程序。

Laravel Forge
Tinkerwell logo

Tinkerwell

Laravel 开发人员必备的代码运行器。使用人工智能、自动完成和对本地和生产环境的即时反馈进行调试。

Tinkerwell
No Compromises logo

无妥协

Joel 和 Aaron,两位来自 No Compromises 播客的经验丰富的开发人员,现在可以为您的 Laravel 项目聘用。 ⬧ 固定费率为每月 7500 美元。 ⬧ 没有漫长的销售流程。 ⬧ 没有合同。 ⬧ 100% 返款保证。

无妥协
Kirschbaum logo

Kirschbaum

提供创新和稳定性,确保您的 Web 应用程序取得成功。

Kirschbaum
Shift logo

Shift

运行的是旧版本的 Laravel?立即自动执行 Laravel 升级和代码现代化,让您的应用程序保持新鲜感。

Shift
Bacancy logo

Bacancy

让经验丰富的 Laravel 开发人员 (4-6 年经验) 为您每月 2500 美元为您的项目增光添彩。获得 160 小时的专业服务和 15 天无风险试用。立即预约电话!

Bacancy
Lucky Media logo

Lucky Media

立即获得好运 - Laravel 开发的理想选择,拥有超过十年的经验!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar:Laravel 电子商务

Laravel 的电子商务。一个开源软件包,将现代无头电子商务功能的强大功能带到 Laravel。

Lunar:Laravel 电子商务
LaraJobs logo

LaraJobs

官方 Laravel 招聘网站

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit:Laravel SaaS 启动工具包

SaaSykit 是一个 Laravel SaaS 启动工具包,它包含运行现代 SaaS 所需的所有功能。支付、精美结账、管理面板、用户仪表板、身份验证、现成组件、统计信息、博客、文档等等。

SaaSykit:Laravel SaaS 启动工具包
Rector logo

Rector

您无缝升级 Laravel、降低成本和加速创新以帮助成功公司的合作伙伴

Rector
MongoDB logo

MongoDB

通过强大的 MongoDB 和 Laravel 集成增强您的 PHP 应用程序,使开发人员能够轻松高效地构建应用程序。支持事务性、搜索、分析和移动用例,同时使用熟悉的 Eloquent API。探索灵活、现代的 MongoDB 数据库如何改变您的 Laravel 应用程序。

MongoDB
Maska is a Simple Zero-dependency Input Mask Library image

Maska 是一个简单的零依赖输入遮罩库

阅读文章
Add Swagger UI to Your Laravel Application image

将 Swagger UI 添加到您的 Laravel 应用程序

阅读文章
Assert the Exact JSON Structure of a Response in Laravel 11.19 image

在 Laravel 11.19 中断言响应的精确 JSON 结构

阅读文章
Build SSH Apps with PHP and Laravel Prompts image

使用 PHP 和 Laravel 提示构建 SSH 应用程序

阅读文章
Building fast, fuzzy site search with Laravel and Typesense image

使用 Laravel 和 Typesense 构建快速、模糊的站点搜索

阅读文章
Add Comments to your Laravel Application with the Commenter Package image

使用 Commenter 包在您的 Laravel 应用程序中添加评论

阅读文章