代码气味是经典。 它闻起来很臭,因为可能有很多情况可以对其进行编辑或改进。 大多数这些气味只是暗示可能有问题。它们本身不需要固定……(不过你应该研究一下。) 以前的代码气味 第一部分 第二部分 第三部分 第四部分 第五部分 第六部分 第七部分 第八部分 第九部分 第十部分 第十一部分 第十二部分 第十三部分 第十四部分 第十五部分 第十六部分 第十七部分 第十八部分 第十九部分 第二十部分 第二十一部分 让我们继续... 代码气味 106 - 生产相关代码 不要添加 IF 检查生产环境。 TL;DR:避免添加与生产相关的条件 问题 Fail fast 违反原则 缺乏可测试性 解决方案 如果完全有必要,对环境进行建模并测试 环境。 所有 语境 有时,我们需要在开发和生产中创建不同的行为。 例如密码的强度。 在这种情况下,我们需要使用强度策略配置环境并测试策略而不是环境本身。 示例代码 错误的 def send_welcome_email(email_address, environment): if ENVIRONMENT_NAME == "production": print(f"Sending welcome email to {email_address} from Bob Builder <bob@builder.com>") else: print("Emails are sent only on production") send_welcome_email("john@doe.com", "development") # Emails are sent only on production send_welcome_email("john@doe.com", "production") # Sending welcome email to john@doe.com from Bob Builder <bob@builder.com> 正确的 class ProductionEnvironment: FROM_EMAIL = "Bob Builder <bob@builder.com>" class DevelopmentEnvironment: FROM_EMAIL = "Bob Builder Development <bob@builder.com>" # 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("john@doe.com", DevelopmentEnvironment()) # Sending welcome email to john@doe.com from Bob Builder Development <bob@builder.com> send_welcome_email("john@doe.com", ProductionEnvironment()) # Sending welcome email to john@doe.com from Bob Builder <bob@builder.com> 检测 手册 [x] 这是一种设计气味。 我们需要创建空的开发/生产配置并使用可定制的多态对象来委托它们。 标签 耦合 结论 避免添加不可测试的条件。 创建委派业务规则的配置。 使用抽象、协议和接口,避免硬层次结构。 关系 代码气味 56 - 预处理器 更多信息 如何避免烦人的ifs 学分 照片由 伯明翰博物馆信托基金会 拍摄 这条推文的灵感来自 @Jan Giacomelli 推特 复杂性是技术不成熟的标志。无论是 ATM 还是爱国者导弹,使用简单是设计精良产品的真正标志。 丹尼尔·T·灵 软件工程名言 Code Smell 107 - 变量重用 重用变量使范围和边界更难遵循 TL;DR:不要为了不同的目的读写同一个变量 问题 可读性 隐藏的问题 解决方案 不要重用变量 以隔离范围 提取方法 语境 在编写脚本时,重用变量是很常见的。 这会导致混乱并使调试更加困难。 我们应该尽可能地缩小范围。 示例代码 错误的 // 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 违反原则 解决方案 避免浮动,除非您有真正的性能问题 使用任意精度数 如果您需要比较浮点数,请与公差进行比较。 语境 比较浮点数是一个古老的计算机科学问题。 通常的解决方案是使用阈值比较。 我们建议完全避免使用浮点数并尝试使用无限精度数。 示例代码 错误的 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 Baumeister Unsplash 上帝创造了自然数;其他一切都是人的工作。 利奥波德·克罗内克 软件工程名言 代码气味 109 - 自动属性 如果结合 4 种代码气味会发生什么? TL;DR:避免 Getter,避免 Setter,避免元编程。想想行为。 问题 信息隐藏违规 可变性 违反 快速失败原则 设置属性时重复代码 解决方案 删除自动设置器和获取器 语境 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 - 贫血模型 更多信息 W3学校 懒惰我 - 元编程 懒惰 II - 代码向导 重构 001 - 移除 Setter 变种人的邪恶力量 快速失败 学分 在 上拍摄的照片 科尼 Unsplash 没有什么比在紧迫的期限内工作并仍然花时间清理工作更难的了。 肯特贝克 软件工程名言 Code Smell 110 - 带默认值的开关 默认意味着“我们还不知道的一切”。我们无法预见未来。 TL;DR:不要在您的案例中添加默认条款。将其更改为异常。明确。 问题 耦合 Fail Fast 违反原则 开闭原则违例 解决方案 用多态替换 if 和 case 将默认代码更改为异常 语境 在使用案例时,我们通常会添加一个默认案例,这样它就不会失败。 失败总是比在没有证据的情况下做出决定要好。 由于 也是一种气味,我们可以避免它们。 外壳和开关 示例代码 错误的 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 Woroniecki Unsplash 添加功能的成本不仅仅是编码所需的时间。成本还包括为未来扩展增加的障碍。诀窍是选择不相互冲突的特征。 约翰·卡马克 软件工程名言 这就是现在…… 下一篇文章将解释另外 5 种代码异味!