美文网首页程序员之言Java 杂谈
用实例告诉你如何重构带有坏味道的代码

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

作者: java面试笔试 | 来源:发表于2018-08-28 10:41 被阅读44次

    作者|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

    公众号:javafirst

    相关文章

      网友评论

        本文标题:用实例告诉你如何重构带有坏味道的代码

        本文链接:https://www.haomeiwen.com/subject/szrfwftx.html