0

0

教你如何更好地重构PHP代码

藏色散人

藏色散人

发布时间:2023-03-13 15:44:27

|

3147人浏览过

|

来源于learnku

转载

本篇文章给大家带来了关于php的相关知识,其中主要跟大家聊一聊什么是重构?怎么更好的重构php代码?感兴趣的朋友下面一起来看一下吧,希望对大家有帮助。

教你如何更好地重构PHP代码

重构指的是在不改变原有功能的情况下,修改或者重新编写代码。

下面的例子中,我将向你展示如何更好地编写代码。

#1 - 表现力

立即学习PHP免费学习笔记(深入)”;

这可能只是一个简单的技巧,但编写富有表现力的代码可以大大改进我们的代码。总是让代码自我解释,这样未来的你或其他开发人员都能知道代码中发生了什么。

不过也有开发人员表示,命名是编程中最困难的事情之一。这就是为什么这不像听起来那么容易的原因之一。 

示例 #1 - 命名

之前

Otter.ai
Otter.ai

一个自动的会议记录和笔记工具,会议内容生成和实时转录

下载
// ❌ 这个方法是用来做什么的,方法名表达并不清晰
// ❌ 是设置状态还是检查状态呢?
$status = $user->status('pending');

之后

// ✅ 通过添加 is,使方法名表达的意图更清晰
// ✅ 检测用户状态是否与给定状态相等
// ✅ 同时新变量名让我们可以推断它是布尔值
$isUserPending = $user->isStatus('pending');

示例 #2 - 命名

之前

// ❌ 这个类返回的是什么?类名?类全名?还是类路径?
return $factory->getTargetClass();

之后

// ✅ 我们获取的是类路径
// ✅ 如果用户想要类名?则找错了方法
return $factory->getTargetClassPath();

示例 #3 - 提取

之前

// ❌ 重复的代码 ( "file_get_contents", "base_path" 方法以及文件扩展)
// ❌ 此刻,我们不去关心如何获得code examples
public function setCodeExamples(string $exampleBefore, string $exampleAfter)
{
    $this->exampleBefore = file_get_contents(base_path("$exampleBefore.md"));
    $this->exampleAfter = file_get_contents(base_path("$exampleAfter.md"));
}

之后

public function setCodeExamples(string $exampleBefore, string $exampleAfter)
{ 
    // ✅ 代码直接说明了我们的意图:获取code example(不关注如何获取)
    $this->exampleBefore = $this->getCodeExample($exampleBefore);
    $this->exampleAfter = $this->getCodeExample($exampleAfter);
}
// ✅ 这个新方法可多次调用
private function getCodeExample(string $exampleName): string
{
    return file_get_contents(base_path("$exampleName.md"));
}

示例 #4 - 提取

之前

// ❌ 多重 where 语句,使阅读变得困难
// ❌ 意图究竟是什么呢?
User::whereNotNull('subscribed')->where('status', 'active');

之后

// ✅ 这个新的scope方法说明了发生了什么事
// ✅ 如果我们需要了解更多细节,可以进入这个scope方法内部去了解
// ✅ "subscribed" scope 方法可在其他地方使用
User::subscribed();

示例 #5 - 提取

这是我之前项目的一个例子。我们用命令行导入用户。 ImportUsersCommand 类中含有一个 handle 方法,用来处理任务。

之前

protected function handle()
{
    // ❌ 这个方法包含太多代码
    $url = $this->option('url') ?: $this->ask('Please provide the URL for the import:');
    $importResponse =  $this->http->get($url);
    // ❌ 进度条对用户很有用,不过却让代码显得杂乱
    $bar = $this->output->createProgressBar($importResponse->count());
    $bar->start();
    $this->userRepository->truncate();
    collect($importResponse->results)->each(function (array $attributes) use ($bar) {
        $this->userRepository->create($attributes);
        $bar->advance();
    });
    // ❌ 很难说清此处发生了哪些行为
    $bar->finish();
    $this->output->newLine();
    $this->info('Thanks. Users have been imported.');
    if($this->option('with-backup')) {
        $this->storage
            ->disk('backups')
            ->put(date('Y-m-d').'-import.json', $response->body());
        $this->info('Backup was stored successfully.');
    }
}

之后

protected function handle(): void
{
    // ✅ handle方法是你访问该类首先会查看的方法
    // ✅ 现在可以很容易就对这个方法做了些什么有个粗略的了解
    $url = $this->option('url') ?: $this->ask('Please provide the URL for the import:');
    $importResponse =  $this->http->get($url);
    $this->importUsers($importResponse->results);
    $this->saveBackupIfAsked($importResponse);
}
// ✅ 如果需要了解更多细节,可以查看这些专用的方法
protected function importUsers($userData): void
{
    $bar = $this->output->createProgressBar(count($userData));
    $bar->start();
    $this->userRepository->truncate();
    collect($userData)->each(function (array $attributes) use ($bar) {
        $this->userRepository->create($attributes);
        $bar->advance();
    });
    $bar->finish();
    $this->output->newLine();
    $this->info('Thanks. Users have been imported.');
}
// ✅ 不要害怕使用多行代码
// ✅ 这个例子中它让我们核心的 handle 方法更为简洁
protected function saveBackupIfAsked(Response $response): void
{
    if($this->option('with-backup')) {
        $this->storage
            ->disk('backups')
            ->put(date('Y-m-d').'-import.json', $response->body());
        $this->info('Backup was stored successfully.');
    }
}

#2 - 提前返回

提前返回指的是,我们尝试通过将结构分解为特定 case 来避免嵌套的做法。这样,我们得到了更线性的代码,更易于阅读和了解。不要害怕使用多个 return 语句。

示例 #1

之前

public function calculateScore(User $user): int
{
    if ($user->inactive) {
        $score = 0;
    } else {
        // ❌ 怎么又有一个 "if"?
        if ($user->hasBonus) {
            $score = $user->score + $this->bonus;
        } else {
            // ❌ 由于存在多个层级,大费眼神 ? 
            $score = $user->score;
        }
    }
    return $score;
}

之后

public function calculateScore(User $user): int
{
    // ✅ 边缘用例提前检测
    if ($user->inactive) {
        return 0;
    }
    // ✅ 每个用例都有自己的代码块,使得更容易跟进
    if ($user->hasBonus) {
        return $user->score + $this->bonus;
    }
    return $user->score;
}

示例 #2

之前

public function sendInvoice(Invoice $invoice): void
{
    if($user->notificationChannel === 'Slack')
    {
        $this->notifier->slack($invoice);
    } else {
        // ❌ 即使是简单的ELSE都影响代码的可读性
        $this->notifier->email($invoice);
    }
}

之后

public function sendInvoice(Invoice $invoice): bool
{
    // ✅ 每个条件都易读
    if($user->notificationChannel === 'Slack')
    {
        return $this->notifier->slack($invoice);
    }
    // ✅ 不用再考虑ELSE 指向哪里
    return $this->notifier->email($invoice);
}

Note: 有时你会听到 “防卫语句” 这样的术语,它是通过提前返回实现。

#3 - 重构成集合 Collection

在 PHP 中,我们在很多不同数据中都用到了数组。处理及转换这些数组可用功能非常有限,并且没有提供良好的体验。(array_walk, usort, etc)

要处理这个问题,有一个 Collection 类的概念,可用于帮你处理数组。最为人所知的是 Laravel 中的实现,其中的 collection 类提供了许多有用的特性,用来处理数组。

注意: 以下例子, 我将使用 Laravel 的 collect () 辅助函数,不过在其他框架或库中的使用方式也很相似。

示例 #1

之前

// ❌ 这里我们有一个临时变量 
$score = 0;
// ❌ 用循环没有问题,不过可读性还是有改善空间
foreach($this->playedGames as $game) {
    $score += $game->score;
}
return $score;

之后

// ✅ 集合是带有方法的对象
// ✅ sum 方法使之更具表现力
return collect($this->playedGames)
    ->sum('score');

示例 #2

之前

$users = [
    [ 'id' => 801, 'name' => 'Peter', 'score' => 505, 'active' => true],
    [ 'id' => 844, 'name' => 'Mary', 'score' => 704, 'active' => true],
    [ 'id' => 542, 'name' => 'Norman', 'score' => 104, 'active' => false],
];
// 请求结果: 只显示活跃用户,以 score 排序  ["Mary(704)","Peter(505)"]
$users = array_filter($users, fn ($user) => $user['active']);
// ❌ usort 进行排序处理的又是哪一个对象呢?它是如何实现?
usort($users, fn($a, $b) => $a['score'] < $b['score']);
// ❌ 所有的转换都是分离的,不过都是users相关的
$userHighScoreTitles = array_map(fn($user) => $user['name'] . '(' . $user['score'] . ')', $users);
return $userHighScoreTitles;

之后

$users = [
    [ 'id' => 801, 'name' => 'Peter', 'score' => 505, 'active' => true],
    [ 'id' => 844, 'name' => 'Mary', 'score' => 704, 'active' => true],
    [ 'id' => 542, 'name' => 'Norman', 'score' => 104, 'active' => false],
];
// 请求结果: 只显示活跃用户,以 score 排序  ["Mary(704)","Peter(505)"]
// ✅ 只传入一次users
return collect($users)
    // ✅ 我们通过管道将其传入所有方法
  ->filter(fn($user) => $user['active'])
  ->sortBy('score')
  ->map(fn($user) => "{$user['name']} ({$user['score']})"
  ->values()
    // ✅ 最后返回数组
  ->toArray();

#4 - 一致性

每一行代码都会增加少量的视觉噪音。代码越多,阅读起来就越困难。这就是为什么制定规则很重要。保持类似的东西一致将帮助您识别代码和模式。这将导致更少的噪声和更可读的代码。

示例 #1

之前

class UserController 
{
    // ❌ 确定如何命名变量(驼峰或是蛇形等),不要混用!
    public function find($userId)
    {
    }
}
// ❌ 选择使用单数或者复数形式命名控制器,并保持一致
class InvoicesController 
{
    // ❌ 修改了样式,如花扣号的位置,影响可读性
    public function find($user_id) {
    }
}

之后

class UserController 
{
    // ✅ 所有变量驼峰式命名
    public function find($userId)
    {
    }
}
// ✅ 控制器命名规则一致(此处都使用单数)
class InvoiceController 
{
    // ✅ 花括号的位置(格式)一致,使代码更为可读
    public function find($userId)
    {
    }
}

示例 #2

之前

class PdfExporter
{
    // ❌ "handle" 和 "export" 是类似方法的不同名称
    public function handle(Collection $items): void
    {
        // export items...
    }
}
class CsvExporter
{
    public function export(Collection $items): void
    {
        // export items...
    }
}
// ❌ 使用时你会疑惑它们是否处理相似的任务
// ❌ 你可能需要再去查看类源码进行确定
$pdfExport->handle();
$csvExporter->export();

之后

// ✅ 可通过接口提供通用规则保持一致性
interface Exporter
{
    public function export(Collection $items): void;
}
class PdfExporter implements Exporter
{
    public function export(Collection $items): void
    {
        // export items...
    }
}
class CsvExporter implements Exporter
{
    public function export(Collection $items): void
    {
        // export items...
    }
}
// ✅ 对类似的任务使用相同的方法名,更具可读性
// ✅ 不用再去查看类源码,变可知它们都用在导出数据
$pdfExport->export();
$csvExporter->export();

重构 ❤️ 测试

我已经提到过重构不会改变代码的功能。这在运行测试时很方便,因为它们也应该在重构之后工作。这就是为什么我只有在有测试的时候才开始重构代码。他们将确保我不会无意中更改代码的行为。所以别忘了写测试,甚至去 TDD。

推荐学习:《PHP视频教程

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

php

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

46

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

178

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

51

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

92

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

227

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

532

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

171

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 13.4万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号