微信号:frontshow

介绍:InfoQ大前端技术社群:囊括前端、移动、Node全栈一线技术,紧跟业界发展步伐。

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

2018-08-24 21:17 无明 译
作者|Mohamed Aladdin
译者|无明
如果出现了代码坏味道,说明你的代码写得不够好,需要重构才能让它们变成干净的代码。在这篇文章中,我将通过 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

 活动 推荐

前端技术发展日新月异,可还是会有面对大型应用的大家各自为战的困扰。想知道前端最新趋势,可以关注 QCon 全球软件开发大会,从实践中积累的前端架构经验,典型的前端框架应用经验,新型框架与设计思路三个方面探索前端研发之路。大会9 折报名中,立减 680 元。有任何问题欢迎咨询票务经理 Hanna,电话:010-84782011,微信:qcon-0410

 
前端之巅 更多文章 Dart重启!Dart2正式发布,目标成为移动与Web开发主流语言 Android P正式发布,你需要尽快做适配了 前端性能之JavaScript成本2018版 前端周报:TypeScript 3.0发布,前端面试指南免费下载 Uber开源Fusion.js:一个基于插件架构的通用Web框架
猜您喜欢 学习之道 | 学习不是枯燥的劳作,是精彩的冒险(评论区留言送书啦!) 用Ansible自动供应vmware虚拟机--构建数据中心一体化运维平台第二篇 Ceph对象存储网关中的索引工作原理 (61) 内存映射文件及其应用 - 实现一个简单的消息队列 / 计算机程序的思维逻辑 API 调用次数限制实现