阅读 275

[Dart翻译]用Dart代码度量标准提高代码质量

原文地址:medium.com/wriketechcl…

原文作者:incendial.medium.com/

发布时间:2021年4月28日

image.png

Dart Code Metrics是一个静态代码分析工具,它允许你收集代码指标并为分析器提供额外的规则。该工具可以帮助开发者监控代码的质量并加以改进。在这篇文章中,我们想和社区分享Dart Code Metrics的功能。这个工具帮助了我们Wrike,我们希望它也能帮助你。

它可以通过命令行启动,作为插件连接到Dart分析服务器,或者作为一个库。通过命令行启动,你可以轻松地将该工具整合到CI/CD流程中,你可以在控制台、HTML、JSON、CodeClimate或GitHub中获得结果。将该工具作为插件连接到分析服务器,可以让你直接从IDE中接收实时反馈。

我们为什么要开发这样一个工具?Wrike已经有大约250万行用Dart编写的代码。像这样的代码库是以维护为代价的--那么你怎么知道什么时候该重构你的代码,你又该从什么开始呢?

Dart SDK分发了一个分析器,它有一个内置的interter,有一套规则。在官方的pub中,你也可以找到推荐的规则集:pedantic, effective_dart , 等等。这有助于我们避免犯错,并按照语言作者的建议维护代码库。然而,我们没有足够的代码分析数据,这也是一切的开始。

衡量标准

研究了关于评估和设计程序代码的材料后,我们意识到第一步是要实现从代码中收集软件指标。

现在,分析器收集了以下指标。

  • 循环复杂性
  • 可执行代码的行数
  • 代码的行数
  • 参数的数量
  • 方法的数量
  • 最大嵌套
  • 类的权重

我们为每个指标设置了一个基本的阈值,之后建议对代码进行重构。同时,当从控制台调用时,或者在分析器的插件配置过程中通过analysis_options.yaml调用时,阈值可以很容易地被预定义并传递给工具。你可以在这里阅读更多关于这个的信息。

在下一步,该工具依靠几个指标的数据找到代码库中的反模式。目前只实现了两个反模式--长方法和长参数列表--但我们将在未来扩展这个列表。

虽然像参数数或方法数这样的指标很容易理解,但循环复杂性呢?

一段代码的循环复杂性是指代码中线性独立路线的数量。例如,如果源代码不包含任何分支点或循环,那么它的复杂性就是1,因为代码中只有一条路线。如果代码中有一个包含一个简单条件的if语句,我们就有两条通过代码的路线:if语句为真或假。

在函数体中的许多独立路线会影响代码的可读性和维护。所以在这种情况下,最好将函数拆分。

该工具提供了各种报告格式来可视化指标。我们将在后面的 "报告 "部分再讨论这个问题。

筛选

最初,该工具只收集指标,但后来我们增加了提示功能。

其他生态系统有一些有用的规则,比如未使用的参数检查、类成员排序检查等等。这些规则在内置的Dart SDK提示器中是不存在的,所以我们做了自己的提示器。

为什么我们决定在一个单独的包中添加linting,而不是在内置的分析器中做PR?因为我们决定实现扩展的配置能力。例如,我们可以配置应该被检查的类成员的确切顺序列表。

当前的规则列表。

一般规则

  • Avoid-unused-parameters
  • binary-expression-operand-order
  • 双重文字格式
  • 成员排序
  • 成员排序--扩展
  • 返回前的换行符
  • 无boolean-literal-compare
  • 无空块
  • 不等于参数
  • 不等于--否则
  • 无魔法数字
  • 无对象声明
  • 倾向于条件表达式
  • 倾向于使用尾部逗号

用于国际语言库

  • 倾向于intl-name
  • 提供正确的intl-args

适用于Dart Angular

  • 避免保留白色空间-false
  • component-annotation-arguments-ordering
  • 偏向于使用推送策略(on-push-cd-strategy)

你可以在文档中找到最新的列表。

风格规则并不是唯一需要考虑的重要事项;我们还想强调一些潜在的错误,如no-equal-then-else,no-equal-arguments,等等。

我们的规则部分是基于我们在审查过程中会遇到的问题。理想情况下,我们希望这些问题能被自动覆盖,这样我们就能专注于重要的工作。我们规则的另一部分是在研究其他工具的规则过程中产生的。(向PVS-Studio、TSLint和ESLint的灵感致敬!)

让我们仔细看看这些规则中的一些。

避免未使用的参数。检查函数或方法的未使用参数。一个未使用的参数可以表明在重构过程中不再需要它,或者一个变量在函数(或方法)的主体中的某个地方,而不是该参数。在第一种情况下,该规则有助于删除未使用的代码;第二种情况表明可能出现错误。

下面是一个简单的例子。

String method(String value) => “”;
复制代码

这里没有使用value参数,分析器会显示 "参数未使用 "的信息。

由于Dart允许你从任何类中继承,并要求参数包含在子类中,所以在基类中可以将其重命名为_,分析器将跳过它。

比如说

String method(String _) => “”;
复制代码

倾向于尾部逗号。检查参数、参数、枚举和集合的尾部逗号,只要它们跨越了多行。

例如。

void firstFunction (String firstArgument, String secondArgument, String thirdArgument) {
  ...
}
复制代码

对于这样的函数,规则会建议在结尾处加一个逗号,这样一来,一旦格式化,它就变成了。

void firstFunction(
  String firstArgument,
  String secondArgument,
  String thirdArgument,
) {
  ...
}
复制代码

如果参数最初被放在一行,分析器不会认为这是一个错误。

void secondFunction(String arg1, String arg2, String arg3) {
  ...
}
复制代码

你也可以为规则指定一个break-on参数,它可以对指定数量的元素进行额外检查。例如,如果没有break-on,上面的例子被认为是正确的。但是如果你把规则配置为break-on:2,分析器将为这个函数显示一个错误,并建议添加一个逗号。

没有相同的参数。检查在实例化一个类或调用一个方法/函数时,同一个参数是否被传递了一次以上。

假设有某个用户类和一个单独的函数来创建这个用户。

class User {
  final String firstName;
  final String lastName;
  
  const User(this.firstName, this.lastName);
}
User createUser(String lastName) {
  String firstName = getFirstName();
  return User(
    firstName,
    firstName,
  );
}
复制代码

这两个User类的字段都是字符串,很容易错过在创建时传递同一个变量。在这种情况下,规则会显示变量firstName被传递了不止一次,这可能是一个错误。

成员排序扩展。该规则检查类成员的顺序。该规则接受了一个扩展的后缀,因为我们已经有了一个成员排序规则,但它并不那么灵活。

该规则接受一个具有相当灵活模板的排序配置。它不仅允许你指定类成员的类型(字段、方法、构造函数),而且还允许你指定关键字,如 late、const、final,或者,例如nullable。

该配置可以是这样的。

  • 公共-晚-最终-字段
  • 私人-晚-最终-字段
  • public-nullable-fields
  • 私有的可归零的字段
  • 命名的结构体
  • 工厂结构体
  • 获取器
  • 设置器
  • 公共静态方法
  • 私人静态方法
  • 受保护的方法
  • 等。

或者被简化为

  • 领域
  • 方法
  • 设置器
  • 获取器(或者,如果不需要分开,就用获取器-设置器)。
  • 构造函数

文档中阅读更多关于规则的能力。

此外,该规则可能需要按字母顺序排序。要做到这一点,你需要在其配置中传递alphabetize: true。

报告

为了使所有收集到的指标和提示结果可视化,该工具方便地提供以下格式的报告。

  • 控制台
  • HTML
  • JSON
  • CodeClimate
  • GitHub

Console格式是默认的,可以通过--reporter标志转移到控制台工具。

当运行该命令时,比如说

$ dart pub run dart_code_metrics:metrics lib
# or for a Flutter package
$ flutter pub run dart_code_metrics:metrics lib
复制代码

当在Dart Code Metrics代码库上执行该命令时,我们会在控制台收到以下报告。

image.png

如果你想选择不同类型的报告(如HTML),你需要运行以下命令。

$ dart pub run dart_code_metrics:metrics lib --reporter=html
# or for a Flutter package
$ flutter pub run dart_code_metrics:metrics lib --reporter=html
复制代码

报告将在metrics文件夹中生成。也可以使用 --output-directory 或 -o 标志传递生成的文件夹。

image.png

在报告中,你可以分别查看每个文件。

image.png

要查看指标的详细信息,你需要将鼠标悬停在代码左边的图标上。

image.png

如果你使用GitHub工作流,并希望在创建的PR中立即获得报告,你需要在管道中添加一个新步骤。

jobs:
  your_job_name:
  ...
    steps:
    ...
     — name: Run Code Metrics
       run: flutter pub run dart_code_metrics:metrics --reporter=github lib
复制代码

这将使你获得这种格式的报告。

image.png

关于所有类型的度量和配置方法的详细信息可以在文档中找到。

安装

如果你想自己尝试一下这个包,这里有一个简短的指南,把它作为Dart分析器的一个插件来连接。

第一步:将软件包作为开发依赖项安装。

$ dart pub add --dev dart_code_metrics
# or for a Flutter package
$ flutter pub add --dev dart_code_metrics
复制代码

或者,手动添加软件包到pubspec.yaml。

重要提示:如果你的软件包还没有被迁移到null safety,请使用2.4.1版本。

dev_dependencies:
  dart_code_metrics: ^3.0.0
复制代码

运行该命令来安装依赖项。

$ dart pub get
# or for a Flutter package
$ flutter pub get
复制代码

第二步:在analysis_options.yaml中添加配置。

analyzer:
  plugins:
    - dart_code_metrics
 
dart_code_metrics:
  anti-patterns:
    - long-method
    - long-parameter-list
  metrics:
    cyclomatic-complexity: 20
    lines-of-executable-code: 50
    number-of-parameters: 4
    maximum-nesting-level: 5
  metrics-exclude:
    - test/**
  rules:
    - newline-before-return
    - no-boolean-literal-compare
    - no-empty-block
    - prefer-trailing-comma
    - prefer-conditional-expressions
    - no-equal-then-else
复制代码

这个配置指定了长方法和长参数列表的反模式,指标及其阈值,以及规则。可用指标的整个列表可以在这里找到,规则的列表在这里

第3步:重新加载IDE,让分析器检测该插件。

如果你想把这个包作为CLI使用,请查看这里的文档。如果想把它作为一个库使用,请看这里的文档。

结论

鉴于Flutter的流行,我们正在考虑哪些指标和规则可以帮助开发者使用这个框架。我们愿意接受建议;如果你有对Dart或Flutter开发者有用的规则或指标的想法,欢迎写信给我们或通过GitHub报告问题。我们很高兴能让开发者的生活变得更好。

我们对未来的计划可以随时在公共项目库的问题列表项目中进行跟踪,在那里你也可以留下对工具的反馈。


通过www.DeepL.com/Translator(免费版)翻译

文章分类
代码人生
文章标签