我希望在开始认真开发之前了解的 5 件事(代码完善之旅)
google-java-格式
编写完成任务的代码很容易。编写优美、称得上干净的代码则可能更加困难。关于干净代码的定义有很多种。有人认为它应该优雅、美观、令人愉悦。另一些人则认为它应该易于阅读。无论你找到哪种定义,你都会注意到,其核心思想是让代码作者关注正在编写的代码。
Bob 大叔说,阅读和编写代码的时间比例是 10:1。因此,确保代码符合本文提到的原则(以及许多其他未提及的原则)至关重要。编写高质量的代码难度更大,耗时更长,但相信我,这是一项值得的投资。
编写代码时可以遵循几条规则,这些规则可以提高代码质量并使您更接近干净的代码掌握。
使用代表你意图的名称
名称是编写代码的基本组成部分。函数、参数、类、文件和文件夹都有名称。使用正确的名称至关重要。把它看作是一项时间投资——虽然需要一些时间,但可以节省更多时间。
函数或类的名称应该能够解答所有潜在的问题——它应该表明它存在的原因、它的功能以及它的用途。如果名称需要注释,则可能表明它并未真正体现其意图。
您会注意到,很难理解该字段的含义(即使有注释作为描述):
m: number; // solving time
m这个名字并没有解释任何东西——它没有表明这个字段的实际用途,甚至注释也不够精确。下面的名称示例要好得多,因为它回答了所有关于它的潜在问题,而且不需要任何额外的解释:
solvingTimeInMinutes: number;
函数也是一样。下面这个函数的用途是什么?
function calculate(value: number) {
return (5 / 9) * (value - 32);
}
虽然它只是一个单行函数,但解释起来却相当困难。这是因为函数名和参数的含义难以理解。以下是相同的函数,但其意图更加明确:
function fahrenheitToCelsius(fahrenheit: number) {
return (5 / 9) * (fahrenheit - 32);
}
要了解有关良好命名实践的更多信息,请搜索:
编程命名约定
避免评论(除非你真的需要它们)
好的注释可以起到关键作用。含糊不清的注释可能会造成误导和混乱。应该避免不必要的注释,因为干净的代码应该是不言自明的,并且能够清晰地表达代码的含义。
以下是一个非常糟糕的评论的例子:
// Always returns true
function returnFalse() {
return false;
}
上面的函数并没有按照注释中说的做。想象一下,在现实场景中使用一个注释错误的函数,后果会很严重,对吧?
以下情况同样如此:
// Checks if player takes part in rating
if (player.contexts.includes(PlayerContext.RankPlayer) && player.isActive())
重构代码并使用类似这样的代码会更好:
if (player.takesPartInRating())
上面的说明比以前的版本清晰得多 - 它清楚地解释了其目的而不需要注释。
但不仅如此——如果代码经常使用注释作为文档,则需要投入更多资源来保持更新。您不仅需要处理代码,还需要修改注释,以确保它们能够体现代码背后的意图。您还必须记住保持注释的更新!
另一个糟糕的做法是将注释掉的代码保留在仓库中。没有什么比这样的代码更糟糕的了:
const player = this.service.getPlayer();
player.initializeSession();
// player.setSessionStartTime();
// player.setNewlyCreatedSession();
其他处理该代码的开发人员会认为注释掉的部分很重要,出于某种原因应该保留。这会导致代码库中保留一些旧的垃圾代码,而这应该避免。与其在代码上添加注释,不如直接删除。
大多数情况下,看到注释意味着代码可以改进,但也有一些例外。其中之一就是 TODO 注释。
// @TODO: integrate with amazing-service once it's ready
function doSomethingAmazing(): void {
(...)
}
TODO 注释指出了应该完成但目前无法完成的任务。它可以提醒用户移除旧功能,也可以提示用户需要解决某些问题。大多数 IDE 都提供快速定位 TODO 注释的功能——应该定期定位并解决这些注释。
另一个有价值的注释的例子是描述代码背后决策的注释。有时我们被迫使用某些框架或浏览器特有的东西——在这种情况下,注释可以起到救命的作用,减少理解代码所需的时间。
// Fix for scroll position computations
// See https://github.com/swimlane/ngx-datatable/issues/669 for details
setTimeout(() =>
this.doSomething();
}, 200);
消除代码中的任何噪音很重要,但也要利用一些有价值且有用的注释的知识。
要了解有关良好注释实践以及如何在不需要注释的情况下编写代码的更多信息,请搜索:
编程注释最佳实践
编程自文档代码
保持代码格式
你只有一次机会给人留下第一印象。因此,你应该真正关注代码格式,因为它是每个阅读代码的人首先看到的东西。它能立即体现代码的质量和一致性。代码应该始终保持完美的格式,每一行都经过精心打磨。
在项目中指定代码格式化规则以保持一致性非常重要。每个项目都不同,因此规则也可能不同,但一旦我们确定了规则,就应该遵循。许多工具可以帮助我们根据规则自动格式化代码。
您觉得下面的代码怎么样?
function doSomethingAmazing () : void
{
const data = this.service.loadData();
data.executeImportantLogic() ;
if(data.somethingHappening)
{
data.executeMoreImportantLogic();
}
this.service.saveData(data);
}
上面的函数编译起来没有任何问题,但它可读性如何?它的格式非常糟糕,根本无法理解。正如你所见,缩进级别没有得到尊重(甚至没有指定),有很多不必要的空格和空行,导致无法有效阅读。
我们可以像这样格式化上面的函数:
function doSomethingAmazing(): void {
const data = this.service.loadData();
data.executeImportantLogic();
if (data.somethingHappening) {
data.executeMoreImportantLogic();
}
this.service.saveData(data);
}
这样就好多了,对吧?
在格式化代码时,务必遵循所用语言的规范。每种语言或框架都有其自身的格式标准,我们应该了解并遵守这些标准。遵守这些标准至关重要,因为它可以帮助新团队成员在项目中编写他们的第一段代码。每增加一条新的自定义格式规则,新手(以及老团队成员)就更难快速适应。
仅仅编写能运行的代码是不够的。为了保持代码整洁,我们需要关注代码的格式,这将提高代码的质量。
要了解有关代码格式的更多信息,请搜索:
代码格式最佳实践
插入您的技术编码风格指南
保持函数简短(并明确其目的)
创建函数的主要原则之一是保持其简短易懂。函数应该易于理解,因为它们只专注于一件事。遵循这条规则将大大减少理解它所需的时间。
它还应该在一个抽象层次上运行,以避免将不太重要的技术细节与关键逻辑混淆。在这种情况下,抽象指的是一个隐喻层面,我们在这个层面上创建函数。如果我们有一个函数用于创建列表并使其递增,那么它将在两个抽象层次上运行:第一个抽象层次用于创建列表,第二个抽象层次用于使其递增。
让我们分析一下下面的函数(但不要在这上面花太多时间):
function solveTask(solution: TaskSolution): void {
const player = this.playerService.getPlayer();
if (!player.gamingProfile) {
this.profileService.createNewProfileForPlayer(player);
this.playerService.startSession();
if (this.settingsService.notificationsEnabled) {
this.notificationService.notify('Player profile session started.');
}
}
const solutions = this.trainingService.getTaskSolutions(player);
if (!solutions.any(sol => sol.id === solution.id)) {
this.trainingService.reportPlayerSolution(solution, player);
if (this.settingsService.notificationsEnabled) {
this.notificationService.notify('Task completed!');
}
const achievementReached = this.achievementService.reportSolutionEvent(solution, player);
if (achievementReached && this.settingsService.notificationsEnabled) {
this.notificationService.notify('Achievement unlocked!');
}
}
}
如你所见,上面的函数并不太长,但它违背了我们之前讨论过的重要原则。首先,它做了不止一件事。其次,它不是在一个抽象级别上运行——它混合了不同的抽象级别。此外,由于相同的代码存在多次,因此存在代码重复。这个函数可以通过以下方式分解来重构:
function solveTask(solution: TaskSolution): void {
const player = this.playerService.getPlayer();
this.handleGamingProfileCreation(player);
this.handleReportingSolvedTask(player, solution);
}
然后使用下面的函数:
function handleGamingProfileCreation(player: Player): void {
if (this.hasGamingProfile(player)) {
return;
}
this.createPlayerGamingProfile(player);
}
function handleReportingSolvedTask(player: Player, solution: TaskSolutin): void {
if (this.alreadySolvedTask(player, solution)) {
return;
}
this.reportSolvedTask(player, solution);
}
函数handleGamingProfileCreation和handleTaskSolving已从原始函数中分解出来。
这样,solveTask函数就只做一件事——解决一个任务。它在其抽象层级上运行,并将其原始逻辑委托给更小、更专注、更易于理解的函数。请注意,每个函数都在不同的抽象层级上运行——一个函数负责创建游戏资料,另一个函数负责报告已解决的任务。
我们所介绍的分解过程在重组(或创建)复杂功能时可能非常方便,并且可以产生更干净、更好的代码。
值得一提的是,并非所有代码都应该分解成小函数。有些情况下,我们可能需要分解出 10 个不同的函数,而这些函数只在分解后的函数中使用。这是一个陷阱,应尽可能避免。您应该根据函数的可读性来决定是否进行分解。如果您对此感到困惑,可以思考以下问题,它们或许有助于您做出决定:
- 其他人会使用该功能吗?
- 分解后的函数可以公开吗?
- 它有助于创建单元测试吗?
- 我是在消除复杂性还是增加复杂性?
要了解有关创建质量函数的更多信息,请搜索:
编程函数的良好实践
编程函数抽象
编程重构分解
成为一名团队成员
提高代码质量的最后一条(但并非最不重要的)规则是团队合作。大多数项目都是由多名开发人员组成的团队开发的。因此,团队合作提高代码质量至关重要。
成为团队成员也意味着将代码视为我们自己的,而不是我的或他们的。每个团队成员都对其质量负有同等责任。在很多情况下,你会遇到过去编写的代码块需要处理,例如命名错误的字段或格式错误的函数。在这种情况下,保留代码比我们看到时更好的状态的规则就变得至关重要——因为我们都在同一个代码库上工作,所以改进别人之前编写的代码并不被禁止。遵守这条规则将极大地提高代码质量,因为它的质量将不断提高。
同样重要的是要记住,尤其是在进行代码审查时,我们审查的是别人的代码,而不是审查本人的代码——这对于保持团队精神和相互尊重至关重要。这种思考代码的方式也有助于与他人共享代码——因为它减少了被其他项目成员冒犯的担忧。它还能增加关于代码本身的讨论,而这正是持续提升代码质量的关键所在。
结论
专业开发人员应该始终编写出最好的代码。这并不容易,这是一个不断学习新知识、不断改进的过程,但这是可行的方法,因为它可以减少许多潜在问题,并提高整体质量。
编写代码是一门艺术——它应该是完美的,但完美并不总是意味着完美。
鏂囩珷鏉簮锛�https://dev.to/emphie/five-things-i-wish-i-knew-before-starting-serious-development-a-journey-into-code-perfection-2e3j