始终如一 - 更快获得结果
始终如一地做事会迫使你明白自己在做什么。然后,一切都会成为习惯。如果你养成了这些好习惯,使它们成为你的自然习惯,那么在以下两种具体情况下,结果会来得更快:
-
正如我们从一开始就看到的那样,你将能够更快地在团队中相互理解—开发人员将拥有相同的习惯。这种情况比较少见,但也有可能发生:有时你需要与项目中的非技术人员讨论代码或向他们展示一些东西。虽然这些人(例如产品负责人)可能具备一些基本的技术知识,但最好还是假设你需要回到最基本的基础知识。如果你在工作中做事简单、干净利落、毫不犹豫,那么向非技术人员解释复杂的技术问题就会容易得多。
-
第二种情况是在自动检查过程中。自动检查是与团队一起制定和讨论的任务,每次要提出修改意见时都要执行。这些检查可以在多个地方进行。可以在软件中编写代码(集成开发环境,如 NetBeans、PhpStorm 或 Visual Studio Code (VS Code)),也可以通过持续集成 (CI) 工具(GitHub Actions、GitLab CI/CD)等。
自动审核任务可以包括你想要的任何任务。我们将在本书 第 7 章 "代码质量工具"(代码质量工具部分)中详细介绍如何执行这些任务,以下是最常见的任务:
-
检查代码风格和缩进
-
运行测试套件(单元测试、功能测试…)
-
对代码进行静态分析,确保使用的变量定义明确,使用的类型正确等。它们使用了正确的类型,等等
-
设置警报,通过选定的渠道(email、Slack 等即时信息)通知你任务的成功或失败、 即时信息,如 Slack 等)
-
部署到测试环境
-
安装依赖项和 vendors
-
将文件复制到服务器并执行一些远程操作
-
随心所欲!
你懂的。这些工具可以让你执行你想要和需要的任务。实际上,它们只是执行你定义的命令的协调器。就是这么简单。之后,如果你的命令很复杂,那就另当别论了。但你会发现,这些自动检查功能是多么无穷无尽。
关于源代码分析工具
只要养成良好的代码习惯,就一定能加快进程。也许最能说明问题的具体例子就是样式代码检查。如果你在团队中不知道如何编写代码,而你的团队又对你想修改的所有内容都进行了自动检查,那么你可能会花上几个小时来弄清在一个地方少了一个空格,在另一个地方少了一个换行符,等等。别担心,大多数工具都提供了自动纠正这些错误的选项。
然而,静态分析工具却不是这样,比如我们将在第 7 章 "代码质量工具" 中专门介绍静态分析工具的章节中设置的静态分析工具。事实上,静态分析会检查代码,确保不会出现最常见的错误。我们说的不是检查制表符中的空格数量,而是真正解析 PHP,试图理解它并确保一切正常。这些工具有时过于严格,需要花费大量时间才能正确理解。另外,这些工具并不完美,静态分析工具可能无法理解你想做的事情。虽然这是一个独立的问题,但如果能从源头上解决这个问题,就能省去很多麻烦:养成良好的编码习惯。要彻底,不要放过任何机会。PHP 是一种非常宽松的语言,它允许你对变量做任何事情—例如,允许你毫不犹豫地进行类型转换。我们知道,这种操作的结果可能是随机的,甚至是非常令人吃惊的,而且可能看起来完全不合逻辑。总之,PHP 就是这样。虽然静态分析工具在大多数情况下都能发现这些危险情况,但你可能要花费数小时来纠正这些小问题,而这些问题的数量可能很快就会达到数十个。
此外(这可能听起来很傻),通过执行清洁代码实践和后续操作,代码越清洁,错误就越少。对自己有信心,就能避免很多惊吓。此外,你还能让下一个开发人员在通过你的代码时不被愚弄,让他们的工作更轻松。如果你足够幸运(或者如果你已经应用了简洁代码的原则及其周围的东西!),你将拥有确保应用程序正常运行的测试。也许你对测试还不熟悉,那就让我们一起来了解一下吧。
关于测试及其多种形式
测试大多是开发人员编写的代码行。这些测试确保某些输入数据会返回特定的输出。这些输入和输出(I/O)的类型和大小各不相同。它可以是整数,也可以是生成的 HTML 页面,甚至可以是图像。为了便于理解和简化概念,自动测试一般分为以下三大类:
-
单元测试是粒度最小的测试。它们通常评估代码中方法的返回值,而忽略其周围的一切。重要的是函数返回的结果,仅此而已。
-
功能测试的粒度适中。它们将评估全面的功能,其中可能涉及多个方面和方法。最明显的例子就是应用程序编程接口(API)的测试:我们要检查的是,如果我们在请求中使用特定参数调用特定的统一资源定位器(URL),API 是否会返回预期的结果。
-
端到端(E2E)测试是最复杂的维护工作。这些测试主要是模拟网络浏览器,并进行自动控制。一个典型的例子就是登录表单测试。机器人会自动填写字段、点击按钮、确保我们被重定向并在 HTML 页面上显示成功信息,等等。
所有这些测试都有一个特定的原因:非回归。
毫无疑问,非回归测试将挽救你作为开发人员在公司中的地位。好吧,也许这有点夸张。但是,我们无法计算有多少应用程序因为非回归测试而得救。当测试覆盖率足够高时(一个或多个测试覆盖的代码行和功能的比例),你几乎可以修改任何东西,而且如果测试总是绿色的,你就可以确保不会破坏任何东西。事实上,你可以破坏功能,以不同的方式重写它们,并测试新的方法。只要测试结果是绿色的,你就可以确保应用程序的行为是正确的,就像你修改之前一样。当然,有几件事情需要注意。
首先,测试必须测试某些东西。这听起来可能很奇怪,但事实上,你会发现很多测试实际上并不评估任何东西。最典型的例子就是对类的设置器和获取器进行单元测试。当你为此编写测试时,你正在评估变量赋值是否已完成,方法调用是否已完成。你正在测试… PHP!PHP 已经有了自己的测试。编写相关的测试本身就是一本书,是一门艺术,可以在多年的实践中不断掌握和完善。为此,没有什么比反复练习更重要了。
修改应用程序时需要考虑的第二件事是,你所设置的测试不再是最新的,必须进行修改。有时,很难理解测试是自愿失败(即与你的更改不兼容)还是非自愿失败(因为应用程序的行为方式与以前不一样,而它本应如此)。这完全取决于你的情况。请记住一点:如果你的应用程序经过了正确的测试,你可以随时闭着眼睛更改任何一行代码并部署你的应用程序,而且不会有任何犹豫。很有趣,不是吗?
现在,你已经了解了在应用程序中进行测试的好处,我们可以讨论一下在清洁代码实践中非常常见的一种做法。你可能已经听说过了: TDD。
TDD 是测试驱动开发的缩写。它是一种在编写其余代码之前先编写测试的方法。起初,它非常令人困惑。要理解它是如何发生的,甚至理解它是如何可能的,都是一件很复杂的事情。它涉及逆向思维,质疑我们的思维习惯,但其原理却非常琐碎。首先,我们要考虑测试,即我们要发送的数据(在单元测试中发送到我们的方法,在功能测试中发送到 API 端点,等等),以及我们想要的输出(在单元测试中是一个精确的对象或值;在 API 功能测试中是一个精确的 JavaScript Object Notation (JSON) 返回值)。显然,由于你尚未编写代码的其余部分,所有测试都会失败。这是故意的。现在的目标是让这些测试一个接一个地变为绿色。
如果你尝试这种做法,你会发现在不知不觉中发生了一种神奇的变化。你会以一种你一开始绝不会采用的方式来组织你的代码。它将以一种清晰而精确的方式被分割开来,这样你的测试就能尽可能快速、轻松地通过。除了智力上的极大满足感,你最终还将获得可读性更强、更简洁的代码。此外(与普遍的看法和直觉相反),开发速度也会大大加快。这确实需要一段时间的适应,才能变得非常自然,而且一开始可能会给人一种非常慢的感觉。不过,正因为如此,你的代码才会更简单,从而更快地编写、理解、调整和扩展。你必须尝试一下才能体会到这一点,因为这听起来可能有点不可思议。从某种程度上来说,确实如此。
此外,在开发过程中,代码覆盖范围会越来越广。这对应用程序的维护有着直接的影响,正如我们之前所说的:你在修改代码时会更有信心,所有需要阅读和修改代码的人也会更有信心。测试将为你提供保护。另外,阅读测试对于理解复杂的代码也是非常有价值的。通过阅读测试,你可以从给出的 I/O 中了解编写测试的开发人员的意图。这在绝大多数应用程序中都是无价之宝,尤其是传统应用程序。此外,很多开发人员在审查你的代码时,首先会看测试。这是了解他人对代码库改动的一个绝佳切入点。将测试视为证明你刚才所做的工作确实有效的唯一途径。这是一个现实:大多数开发人员都对简洁的代码很敏感,他们都认为测试是证明你的修改有效的唯一有价值的证据。
例如,对于大多数(如果不是全部)开源项目(如 PHP)来说,在进行修改时必须添加测试。无论是添加新功能还是修复问题,测试都是强制性的,未经测试,你的更改绝不会获得批准。这些测试将再次无可辩驳地证明你的新功能的行为或你确实修复了相关错误。所有这一切都需要大量的工作,但有了这些工作,代码覆盖范围就会变得非常大,你就能为软件的稳定性做出充分的贡献。
测试对于提高代码质量和加快取得结果的速度都是非常宝贵的。有了它,你将保持一致性,更快地获得结果。
总结
刚才我们一起学习了很多新知识。如果你理解了这些知识,可以肯定你已经是比上一章更好的开发人员了。
了解 SOLID 原则是专业领域和高质量工业项目的真正财富。即使每种情况都不同,每个项目都有其特殊性,但这些原则的优点是几乎在任何地方都适用,至少可以从中得到很强的启发。
牢记 "KISS"、"DRY "和 "YAGNI" 原则可以让你在接下来的开发过程中脚踏实地,不至于过于分散。它们强调思考当下,为未来做准备,而不是思考未来,试图适应当下。你一定要记住这一点。我们不知道未来会有什么样的限制,无论是技术上的还是功能上的,因此更有意义的做法是思考如何让我们更轻松地应对这些限制,而不是去猜测它们。因为面对现实吧:我们很少有机会击中靶心。
如果你有机会和可能,在 TDD 策略中实施 "童子军原则" 会带来更多益处,而且永远是个好主意。如果你从未实践过 TDD,尽管本章已经给出了解释,但对其有用性,尤其是其有效性完全持怀疑态度是很正常的。这很正常,我们都经历过这种情况。然而,结果是存在的,对这一主题进行的各种案例研究也充分证明了这一点。也许是时候对这种做法进行测试了,清洁代码的前辈们对这种做法非常认可和赞赏!
尽管如此,我们必须牢记,简洁代码也要适应环境。这并不是以项目之外的人决定这样做为借口,完全重写应用程序并改变开发团队的所有习惯。你必须了解你所处的环境并与之打交道。你必须能够适应需要和周围的环境。这样才能成为一名优秀的 "干净的编码员"。记住,当意识到要改变习惯时,要尽可能与团队沟通,并能证明自己所有的选择都是正确的。如果可能,最好能提出几种解决方案,并说明每种方案的优缺点。
说完这些理论,我们就可以进入稍微实际一点的部分了。编写简洁代码的方法有哪些?代码的目的是什么?虽然我们已经了解了一些高级原则,但我们不应忘记基础知识,我们还应该质疑我们已经知道的东西。