paint-brush
如何找到代码中的臭味部分 [第 XXII 部分]经过@mcsee
783 讀數
783 讀數

如何找到代码中的臭味部分 [第 XXII 部分]

经过 Maximiliano Contieri8m2022/08/29
Read on Terminal Reader
Read this story w/o Javascript

太長; 讀書

它闻起来很臭,因为可能有很多情况可以对其进行编辑或改进。

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - 如何找到代码中的臭味部分 [第 XXII 部分]
Maximiliano Contieri HackerNoon profile picture

代码气味是经典。

它闻起来很臭,因为可能有很多情况可以对其进行编辑或改进。


大多数这些气味只是暗示可能有问题。它们本身不需要固定……(不过你应该研究一下。)


以前的代码气味

让我们继续...


代码气味 106 - 生产相关代码

不要添加 IF 检查生产环境。

TL;DR:避免添加与生产相关的条件

问题

  • Fail fast 违反原则
  • 缺乏可测试性

解决方案

  1. 如果完全有必要,对环境进行建模并测试所有环境。

语境

有时,我们需要在开发和生产中创建不同的行为。

例如密码的强度。

在这种情况下,我们需要使用强度策略配置环境并测试策略而不是环境本身。

示例代码

错误的

def send_welcome_email(email_address, environment): if ENVIRONMENT_NAME == "production": print(f"Sending welcome email to {email_address} from Bob Builder <[email protected]>") else: print("Emails are sent only on production") send_welcome_email("[email protected]", "development") # Emails are sent only on production send_welcome_email("[email protected]", "production") # Sending welcome email to [email protected] from Bob Builder <[email protected]>

正确的

class ProductionEnvironment: FROM_EMAIL = "Bob Builder <[email protected]>" class DevelopmentEnvironment: FROM_EMAIL = "Bob Builder Development <[email protected]>" # We can unit test environments # and even implement different sending mechanisms def send_welcome_email(email_address, environment): print(f"Sending welcome email to {email_address} from {environment.FROM_EMAIL}") # We can delegate into a fake sender (and possible logger) # and unit test it send_welcome_email("[email protected]", DevelopmentEnvironment()) # Sending welcome email to [email protected] from Bob Builder Development <[email protected]> send_welcome_email("[email protected]", ProductionEnvironment()) # Sending welcome email to [email protected] from Bob Builder <[email protected]>

检测

  • [x]手册

这是一种设计气味。

我们需要创建空的开发/生产配置并使用可定制的多态对象来委托它们。

标签

  • 耦合

结论

避免添加不可测试的条件。

创建委派业务规则的配置。

使用抽象、协议和接口,避免硬层次结构。

关系

代码气味 56 - 预处理器

更多信息

学分

照片由伯明翰博物馆信托基金会拍摄

这条推文的灵感来自@Jan Giacomelli

推特


复杂性是技术不成熟的标志。无论是 ATM 还是爱国者导弹,使用简单是设计精良产品的真正标志。

丹尼尔·T·灵

软件工程名言


Code Smell 107 - 变量重用

重用变量使范围和边界更难遵循

TL;DR:不要为了不同的目的读写同一个变量

问题

  • 可读性
  • 隐藏的问题

解决方案

  1. 不要重用变量
  2. 提取方法以隔离范围

语境

在编写脚本时,重用变量是很常见的。

这会导致混乱并使调试更加困难。

我们应该尽可能地缩小范围。

示例代码

错误的

// print line total double total = item.getPrice() * item.getQuantity(); System.out.println("Line total: " + total ); // print amount total total = order.getTotal() - order.getDiscount(); System.out.println( "Amount due: " + total ); // variable is reused

正确的

function printLineTotal() { double total = item.getPrice() * item.getQuantity(); System.out.println("Line total: " + total ); } function printAmountTotal() { double total = order.getTotal() - order.getDiscount(); System.out.println( "Amount due: " + total ); }

检测

  • [ ]自动

Linter 可以使用解析树来查找变量定义和用法。

标签

  • 可读性

结论

避免重复使用变量名。使用更具体和不同的名称。

关系

代码异味 03 - 函数太长

更多信息

重构 002 - 提取方法

学分

西格蒙德Unsplash上拍摄的照片


先简单后通用,先用后重用。

凯夫林·亨尼

软件工程名言


Code Smell 108 - 浮点断言

断言两个浮点数相同是一个非常困难的问题

TL;DR:不要比较浮点数

问题

  • 错误的测试结果
  • 脆弱的测试
  • Fail fast 违反原则

解决方案

  1. 避免浮动,除非您有真正的性能问题
  2. 使用任意精度数
  3. 如果您需要比较浮点数,请与公差进行比较。

语境

比较浮点数是一个古老的计算机科学问题。

通常的解决方案是使用阈值比较。

我们建议完全避免使用浮点数并尝试使用无限精度数。

示例代码

错误的

Assert.assertEquals(0.0012f, 0.0012f); // Deprecated Assert.assertTrue(0.0012f == 0.0012f); // Not JUnit - Smell

正确的

Assert.assertEquals(0.0012f, 0.0014f, 0.0002); // true Assert.assertEquals(0.0012f, 0.0014f, 0.0001); // false // last parameter is the delta threshold Assert.assertEquals(12 / 10000, 12 / 10000); // true Assert.assertEquals(12 / 10000, 14 / 10000); // false

检测

  • [ ]自动

我们可以在我们的测试框架上添加一个检查 con assertEquals()以避免检查浮点数。

标签

  • 测试气味

结论

我们应该始终避免比较浮点数。

关系

Code Smell 71 - 伪装成小数的魔法浮点数

更多信息

学分

照片由Mika BaumeisterUnsplash上拍摄


上帝创造了自然数;其他一切都是人的工作。

利奥波德·克罗内克

软件工程名言


代码气味 109 - 自动属性

如果结合 4 种代码气味会发生什么?

TL;DR:避免 Getter,避免 Setter,避免元编程。想想行为。

问题

解决方案

  1. 删除自动设置器和获取器

语境

Setter 和 getter 是一种不好的行业惯例。

许多 IDE 都喜欢这种代码味道。

一些语言为构建贫血模型和 DTO 提供了明确的支持。

示例代码

错误的

class Person { public string name { get; set; } }

正确的

class Person { private string name public Person(string personName) { name = personName; //imutable //no getters, no setters } //... more protocol, probably accessing private variable name }

检测

  • [ ]自动

这是一个语言特性。

我们应该避免不成熟的语言或禁止他们最糟糕的做法。

标签

  • 封装

结论

在暴露我们的属性之前,我们需要仔细考虑。

第一步是停止思考属性,只关注行为。

关系

代码气味 28 - 二传手

代码气味 68 - 吸气剂

Code Smell 70 - 贫血模型生成器

代码气味 40 - DTO

代码气味 01 - 贫血模型

更多信息

学分

科尼Unsplash上拍摄的照片


没有什么比在紧迫的期限内工作并仍然花时间清理工作更难的了。

肯特贝克

软件工程名言


Code Smell 110 - 带默认值的开关

默认意味着“我们还不知道的一切”。我们无法预见未来。

TL;DR:不要在您的案例中添加默认条款。将其更改为异常。明确。

问题

解决方案

  1. 用多态替换 if 和 case
  2. 将默认代码更改为异常

语境

在使用案例时,我们通常会添加一个默认案例,这样它就不会失败。

失败总是比在没有证据的情况下做出决定要好。

由于外壳和开关也是一种气味,我们可以避免它们。

示例代码

错误的

switch (value) { case value1: // if value1 matches the following will be executed.. doSomething(); break; case value2: // if value2 matches the following will be executed.. doSomethingElse(); break; default: // if value does not presently match the above values // or future values // the following will be executed doSomethingSpecial(); break; }

正确的

switch (value) { case value1: // if value1 matches the following will be executed.. doSomething(); break; case value2: // if value2 matches the following will be executed.. doSomethingElse(); break; case value3: case value4: // We currently know these options exist doSomethingSpecial(); break; default: // if value does not match the above values we need to take a decision throw new Exception('Unexpected case ' + value + ' we need to consider it'); break; }

检测

  • [x]半自动

我们可以告诉我们的 linter 警告我们默认使用,除非有例外。

标签

  • 快速失败

结论

编写健壮的代码并不意味着我们需要在没有证据的情况下做出决定。

关系

Code Smell 36 - Switch/case/elseif/else/if 语句

更多信息

学分

照片由Joshua WoronieckiUnsplash上拍摄


添加功能的成本不仅仅是编码所需的时间。成本还包括为未来扩展增加的障碍。诀窍是选择不相互冲突的特征。

约翰·卡马克

软件工程名言



这就是现在……

下一篇文章将解释另外 5 种代码异味!