实例讲解 PHP 中的『代码整洁之道』和『代码异味』
代码异味是很常见的标志,这表明你的代码还不够好,需要重构才能最终得到整洁的代码。在本文中,我将用来自 GitHub 上真实项目的真实示例来解释代码异味,并向您展示重构这些坏味道代码以及使之整洁的最佳方法。我还建议你之后能读下这篇文章。 “高级 PHP 工程师必备的编码技巧及思维 ”
- 重复代码及逻辑代码异味
开发人员有点懒惰是很常见的,这在很多方面都很好;但是,懒惰和复杂/粘贴大量代码行并不是一种正确的行为。它可能会导致最常见的代码异味,即逻辑重复,如下面的示例所示。
因此,为避免代码异味,我们需要将红色部分提取到一个单独的方法中,这将有助于我们在有需要的情况下重用它。
- 冗长方法及类代码异味
我相信,当我们需要给一个现有方法添加更多的if()或for()语句,以验证用户输入或确认用户是否已登录时,我们都犯了这个错误(单一方法或类代码过长)。简而言之,你不应该那样做。如果你需要这样的验证,那么创建它自己单独的方法。完美的方法应该在4-20行之间。如果超过了20行,你也许可以将几行代码提取到一个新的方法中去。同样的规则在这里也适用于类,(代码量)更小的则更好,尤其是在应用单一责任原则时。
- 在相同或不同类中存在重复方法的代码异味
另一种代码异味是在你有两个方法都是实现相同功能时需要予以考虑的。下面的图示应该能很好地说明问题。
- 类发散式变化的代码异味
如果你以前有阅读过SOLID原则,尤其是单一职责原则,你就会明白改变一个类只能有一个原因。这意味着一个User类不应该拥有一个涉及产品或文件转换的方法。你可以通过提取非相关方法到一个新类(如Product类或FileSystem类),很容易地修复这种代码异味。
- 散弹枪手术式的代码异味
这与发散式变化正好相反。这种代码坏味道将使你出于一个原因而更改多个类,即意味着多个类会为了同一原因更改以应用一个行为。例如,你需要新建一个用户规则(如超级管理员),你发现自己必须修改 Profile,Products 及 Employees 类中的多个方法。在这种情况下,应当考虑将这些方法组织到一个类中以便于这个新类只有一个改动的理由。
- 「依恋情结」式的代码异味
有时候你发现在自己的类中的一个方法里大量使用了另一个类。在这种情况下,你可以考虑将这个方法移动到它使用的那个类中。如下一张图所示。如果将getFullAddress()变成ContactInfo类的一部分而不是User类将会是更好的情况吧,因为该方法调用的全都是ContactInfo类的方法。
- 大量数据块式的代码异味
有时候你发现许多函数几乎采用了相同的参数列表。正是这种经常一起使用的参数,导致了大量数据块式的代码异味。再看下一个案例;你会发现几乎所有类型的预定都要求提供护照信息。
在这种情况下,最好将 passport 的信息移动到它自己的类中,然后将 PassportInfo 对象传递给预订方法。这是代码重用的一个很好的例子,请记住,长参数列表可能会导致代码运行失败、冲突和困难的单元测试。
- 原始类型困扰代码异味
当您开始在应用程序的任何地方使用原始数据类型时,就会出现这种代码异味。例如,使用整数作为电话号码,使用字符串作为货币符号。如果您是这种情况,请查看下面的类。
可以看到,这里的地址被定义成一个数组。这样会导致两个问题。例如,每次我们需要地址的时候,就必须对其进行硬编码。因此,为什么不直接创建一个 Address 的新类。
现在,每一次我们需要增加或者编辑一个地址,我们就点击 Address 类。另外,每当我们需要增加新的「contact us」方法,就点击 ContactUs 类。这样的话,每个类都有一个单一的职责。
- 开关语句的代码异味
可能你想知道为什么 switch 语句很糟糕。当然,它并不总是坏的。但是如果你注意到下一个示例,switch 语句很大,并且本质上是不可提取的。你不能将其分为较小的方法。
记住,如果你的 switch 不是很大,你可以移除它。switch 一个很好的例子就是使用工厂设计模式替代它。
- 并行继承层次结构的代码异味
有时候我想知道并行继承层次结构是否真的糟糕。我们先了解一下什么是并行继承层次结构,再去判断。
从上面这张图片你注意到每一次我们创建一个 department 类的同时,也需要去创建一个 privilege 类,这时候代码味道就不香了。
- 惰性类的代码异味\
惰性类就是不做那么多事情的类。您还记得上面的这个图片吗?
我们决定将 address 迁移到另外一个类, 但是我们没有对 hotLine 方法做同样的事,为它仅仅只有三行,因此,每当您发现这些惰性类时,就应该消除它们。
- 临时域代码异味
当您有一个仅在某些时候使用过的类实例变量时,就会发生临时域代码异味。看下一个例子。您会注意到 $name 和 $contactDetails 仅在 notify()方法中使用。
那么为什么不将它们作为方法参数传递呢?
- 信息链的代码异味
当一个类使用另一个类,而另一个类又使用其他类的时候,信息链中就有了代码异味。从下图中你可以看到这样的链接: Employee->EmployeeConfig->Config
你可以缩短链接,从而使代码更加简洁,就像下面这样
Employee->Config
- 不适当的亲密代码气味
有时在一个类中发现一个方法,该方法需要对另一个类的内部工作或内部数据了解得太多。正如您在下一个示例中看到的,notify() 方法位于User类中;但是,它使用了 UserContactDetails 类的许多内部方法。
在这种情况下,最好将这个逻辑从 User 类移到 UserContactDetails 类并创建 getWelcomeMessage($userName)。
- 中间人代码气味
有时,您会发现一个类中的许多方法除了委托给另一个类中的另一个方法之外什么也不做。在这种情况下,第一阶级被认为是中产阶级,大多数时候最好摆脱它。
注意:中产阶级在某些情况下可能会有帮助,比如在外观设计模式中。
- 具有不同接口代码气味的替代类
通常,这是因为团队之间缺乏通信,因为创建了两个不同的类,它们执行相同的操作,这意味着代码重复。
- 不完整的库类代码异味\
第三方库并不总是为您提供应用程序中所需的所有功能。在下面示例中,处理文档的库可以通过其ID检索一个文档或一次检索所有文档。
那么,如果需要检索特定用户的所有文档,会发生什么情况?记住,如果你试图自己编辑第三方类,那是很可怕的。在这种情况下,您需要在不编辑原始类的情况下扩展 Document 类的功能。嗯,装饰器设计模式在这里很有帮助,如您在下图中所见。
现在,您应该开始使用 DocumentsDecorator 类而不是 Documents 类。
- 关于注释
我知道你可能会感到惊讶,是的,如果注释被用错了地方,那注释就是坏的代码,所以这里是我的一些提示:
- 删除不必要的注释。
- 如果代码很容易理解,就不要写注释。
- 不要留下注释过的旧代码。
*删除注释调试 var_dump,echo ....etc等。
- 预测常见代码异味
这个代码异味是关于过早地优化的,许多开发人员认识不到它。在优化过程中,需要考虑的一些注意事项是:
- 不要过度优化代码。
- 不要试图覆盖将来有 1% 可能性发生的情况。
- 牺牲一些速度来使算法更加简单,尤其是当你不需要应用程序实时输出结果时。
- 当你的应用程序实际上很慢,而不是只有 100 个用户时,再去优化速度。
现在,我们已经介绍了关于代码异味与清理他们的最佳实践,所有你应该已经准备好编写非常干净的代码并重构你的旧类了。祝你一切顺利!
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
Code Smells 应翻译为 『代码异味』兄弟们请自行修改下
"现在,您应该开始使用 DocumentsDecorator 类而不是 Documents 类。" 不要用装饰器呢,如果有100万的文章,那个装饰器不就把100万的文章取出来了?正确做法是传入$filter数组,按条件进行过滤