用实例告诉你如何重构带有坏味道的代码

开发 后端
如果出现了代码坏味道,说明你的代码写得不够好,需要重构才能让它们变成干净的代码。在这篇文章中,我将通过 GitHub 上的真实项目来解释代码坏味道,并向你展示如何重构这些带有坏味道的代码。

[[241428]]

如果出现了代码坏味道,说明你的代码写得不够好,需要重构才能让它们变成干净的代码。在这篇文章中,我将通过 GitHub 上的真实项目来解释代码坏味道,并向你展示如何重构这些带有坏味道的代码。

重复代码和重复逻辑

开发人员通常很懒惰,在某种程度上,这不算一件坏事。然而,因为懒惰而走上了复制黏贴代码的不归路那就不对了。这样可能会导致最常见的代码坏味道,即逻辑重复,如下所示。

为了摆脱这种代码坏味道,我们需要将红色部分提取到一个单独的方法中,这样就可以在其他地方重用它们。

长方法和臃肿的类

我们都会犯这样的一个错误:在现有方法中添加 if() 或 for() 语句来验证用户输入或检查用户是否已登录。我们其实不应该这样做。如果一定要做这些验证,应该创建自己的方法。方法长度应该在 4 到 20 行之间,如果超过 20 行,可以将其中的几行提取到另一个方法中。同样的规则也适用于类,根据单一责任原则,方法或类越小越好。

相同或不同类中的重复方法

另一个代码坏味道是多个方法提供了相同的功能,如下图所示。

 

分散式变更(Divergent Change)

如果你了解 SOLID 原则,特别是单一职责原则,那么你就应该知道,修改一个类的理由应该是单一的。也就是说,User 类不应具有与产品或文件转换相关的功能。你可以通过将不相关的方法提取到 Product 类或 FileSystem 类来清除这个代码坏味道。

散弹式变更(Shotgun Surgery)

这与发散变更完全相反。这种代码坏味道会让你因为一个需求而去修改多个类。例如,你想要创建一个新的用户规则(如“Supper-Admin”),然后你发现,为了增加这个规则还需要修改 Profile、Products 和 Employees 类中的某些方法。在这种情况下,可以考虑将这些方法放在一个单独的类中。

依恋情结(Feature Envy)

有时候,你会在类中找到一个大量使用另一个类的方法。在这种情况下,你可以考虑将这个方法移动到它使用的那个类中。如下图所示。将 getFullAddress() 从 User 类移动到 ContactInfo 类中岂不是更好?因为它调用了 ContactInfo 类的很多方法。

 

数据泥团(Data Clumps)

有时候,你会发现很多函数具有相同的参数列表,这样会导致数泥团代码坏味道。看看下面的例子,你会发现,几乎所有类型的预订都需要护照信息。

 

在这种情况下,将护照信息移到 PassportInfo 类中,然后将 PassportInfo 对象传给预订方法,这样会更好。这是一个很好的重用代码的例子。请记住,参数列表太长可能更容易导致 bug 和代码冲突,而且难以进行单元测试。

 

痴迷基本类型(Primitive Obsession)

当你在应用程序的所有地方都使用基本数据类型时,就会出现这种代码坏味道。例如,使用整数表示电话号码,使用字符串表示货币符号。如果你是这么做的,那么请先看一下下面这个类。

 

代码中的地址被定义为数组,这样会导致两个问题,例如,每次我们需要使用地址时都要对其进行硬编码。那么,为什么不创建一个 Address 类呢?

 

现在,每次我们需要添加或编辑地址时,只需要修改 Address 类。此外,如果我们需要添加一个新的“联系我们”方法,就增加一个新的 ContactUs 类。这样,每个类都有自己的单一职责。

switch 语句

或许你很想知道为什么使用 switch 语句其实是件很糟糕的事情。虽说使用 switch 语句并不一定总是不好的,但在下面的示例中,你可以看到,switch 语句的代码块不仅很大而且是不可提取的。当代码块变得越来越大时,你将无法将其拆分成更小的方法。

 

如果你的 switch 语句代码块不是很大,那么你可以继续使用它们。例如,工厂模式就使用了 switch 语句。

 

并行继承

有时候我会想,并行继承是不是一种不好的做法。先让我们来解释一下并行继承的概念,然后再讨论它是不是代码坏味道。

 

从上图中可以看出,每当我们创建一个新的部门类时,我们还需要创建一个权限类,这将导致之前提到的散弹式变更代码坏味道。

懒惰类

懒惰类是指只做很少事情的类。还记得这些下面这些代码吗?

 

 

我们将地址移到一个单独的类中,但我们没有对热线也这么做,因为它可能只有 3 行代码。所以,一旦你发现了这些懒惰类,应该将它们移除。

临时字段

当一个类实例的某些变量只是偶尔用到,就出出现临时字段代码坏味道。请看下面的例子,你会注意到 $name 和 $contactDetails 只在 notify() 方法中用到。

 

那么为什么不将它们作为方法参数进行传递呢。

 

消息链

当一个类使用了另一个类,而那个类又使用了另外一个类,并以此类推,那么就会出现消息链代码坏味道。在下图中,你可以看到 Employee->EmployeeConfig->Config。

 

你可以通过缩短链(变成 Employee->Config)让代码变得更整洁。

 

不恰当的亲密关系

有时候,一个类的某个方法需要过多地了解另一个类的内部状态或数据。正如你在下图中所看到的,notify() 方法位于 User Class 中,但它却使用了 UserContactDetails 类的很多内部方法。

 

在这种情况下,***可以将这些逻辑从 User 类移动到 UserContactDetails 类中,并新增 getWelcomeMessage($userName) 方法。

 

中间人

有时候,你会发现一个类的很多方法什么事都不做,只是将调用委托给另一个类。在这种情况下,这个类被认为是中间人,大多数时候可以避免使用它。

 

注意:在 Facade 设计模式中,中间人在某些情况下可能会有所帮助。

接口不同但目的相同的类

通常,因为团队之间缺乏沟通,创建了两个不同的类,但它们的作用却是一样的,这意味着出现了重复代码。

不完整的库

第三方库并不总能为你提供应用程序中所需的所有功能。在下面的示例中,处理文档的库可以通过 ID 获取一个文档或一次获取所有的文档。

 

如果你需要获取特定用户的所有文档该怎么办?在这种情况下,你需要扩展 Document 类的功能,而不直接修改原始类。这个时候可以使用装饰模式,如下图所示。

 

现在,你可以使用 DocumentsDecorator 类而不是 Documents 类。

注释

你可能会感到惊讶,但错误地使用注释也是一种代码坏味道。下面是我的一些建议:

  • 删除不必要的注释。
  • 如果代码很容易理解,请不要添加额外的注释。
  • 不要留下被注释的旧代码。
  • 删除用于调试的注释,如 var_dump、echo 等。

夸夸其谈未来性(Speculative Generality)

这个代码坏味道与过早进行优化有关,很多开发人员都没有注意到这一点。在规划期间需要考虑的一些注意事项:

  • 不要过度计划你的代码。
  • 不要试图涵盖只有 1%可能性会在未来发生的情况。
  • 为了让算法更简单,可以牺牲一些速度,特别是当你不需要应用程序立即给出结果的时候。
  • 当应用程序速度很慢,即使只有 100 个用户,也需要进行优化。

英文原文:https://codeburst.io/write-clean-code-and-get-rid-of-code-smells-aea271f30318 

【责任编辑:庞桂玉 TEL:(010)68476606】

责任编辑:庞桂玉 来源: 前端之巅
相关推荐

2020-06-12 08:21:58

JavaScript代码开发

2019-10-11 09:07:46

Java代码对象

2024-09-05 10:17:34

2012-07-13 09:35:58

PHP

2012-07-13 09:38:15

项目代码

2015-07-29 13:22:40

.NET代码

2021-05-26 11:50:37

代码优化Java

2012-07-19 10:42:17

项目

2022-01-26 10:29:24

微服务循环依赖代码

2012-07-27 10:30:12

重构

2024-12-11 18:24:29

2022-03-10 09:49:58

重构代码

2019-04-03 08:10:17

代码架构信息

2024-09-27 12:04:48

2015-10-23 09:35:57

融资YC创始人投资

2022-05-07 10:01:20

好代码坏代码

2020-12-01 08:36:10

代码程序员函数

2014-11-05 10:58:00

编程

2016-09-25 16:02:09

《硅谷》编程虚拟内存

2021-05-19 08:55:37

代码程序员经验分享
点赞
收藏

51CTO技术栈公众号