没有明显抽象的代码重复


14

您是否曾经遇到过代码重复的案例,在这种情况下,在查看代码行时,您无法对其进行专题描述以忠实地描述其在逻辑中的作用?您做了什么处理呢?

这是代码重复,因此理想情况下,我们需要进行一些折光处理,例如使其具有自己的功能。但是由于代码没有一个很好的抽象来描述它,所以结果将是一个奇怪的函数,我们甚至无法为其找到一个好名字,而且从逻辑上看,它在逻辑中的作用并不明显。对我来说,这损害了代码的清晰度。我们可以保留清晰度并保持原样,但随后会损害可维护性。

您认为解决此类问题的最佳方法是什么?

Answers:


18

有时,代码重复是“双关”的结果:两件事看起来相同,但事实并非如此。

过度抽象可能会破坏系统的真正模块化。在模块化的机制下,您必须决定“可能会发生什么变化?” 和“什么是稳定的?”。稳定的对象都放入接口中,而不稳定的对象都封装在模块的实现中。然后,当事情确实发生变化时,您需要进行的更改将与该模块隔离。

当您认为稳定的内容(例如,此API调用将始终带有两个参数)需要更改时,必须进行重构。

因此,对于这两个重复的代码片段,我会问:对一个进行必要的更改是否必然意味着必须对另一个进行更改?

您如何回答该问题可能使您更好地了解什么是好的抽象。

设计模式也是有用的工具。也许您重复的代码正在执行某种形式的遍历,并且应该应用迭代器模式。

如果重复的代码具有多个返回值(这就是为什么您不能执行简单的提取方法),那么也许您应该制作一个包含返回值的类。该类可以为在两个代码片段之间变化的每个点调用抽象方法。然后,您将对该类进行两个具体的实现:每个片段一个。[这实际上是模板方法设计模式,不要与C ++中的模板概念混淆。另外,使用策略模式可能会更好地解决您正在查看的内容。]

考虑它的另一种自然而有用的方法是使用高阶函数。例如,制作lambda或使用匿名内部类将代码传递给抽象。通常,您可以删除重复项,但是除非它们之间确实存在关系(如果其中一项发生更改,另一项也必须更改),则可能会损害模块化,而无济于事。


4

当您遇到这种情况时,最好考虑一下“非传统”抽象。也许您在一个函数中有很多重复项,并且分解一个普通的旧函数不太适合,因为您必须传递太多的变量。在这里,D / Python样式的嵌套函数(可以访问外部作用域)会很好用。(是的,您可以使一个类保留所有状态,但是,如果仅在两个函数中使用它,则这是一个丑陋且冗长的解决方法,因为它没有嵌套函数。)也许继承不太合适,但是继承mixin效果很好。也许您真正需要的是一个宏。也许您应该考虑一些模板元编程或反射/自省,甚至生成性编程。

当然,从务实的角度来看,如果您的语言不支持它们并且没有足够的元编程功能来在语言中清晰地实现它们,那么即使不是不可能,也很难做到。如果是这种情况,除了“获取更好的语言”之外,我不知道该告诉您什么。另外,学习具有许多抽象功能的高级语言(例如Ruby,Python,Lisp或D)可能会帮助您更好地使用一些技术仍然可用但不太明显的低级语言进行编程。


+1是将许多出色的技术压缩在狭窄空间中的方法。(嗯,所描述的技术也应该是+1。)
Macneil

3

我个人不理会它,继续前进。如果情况很奇怪,最好将其复制,您可以花一些时间进行重构,而下一个开发人员将一目了然并撤消您的更改!


2

没有代码示例,很难说为什么您的代码没有容易识别的抽象。有了这些警告,这里有一些想法:

  • 与其创建一个新功能来保存通用代码,不如将功能分成几个不同的部分;
  • 根据常见的数据类型或抽象行为将小片段组合在一起;
  • 根据新片段重写重复的代码;
  • 如果新代码仍然不符合清晰的抽象要求,则也将其分解为小部分,然后重复该过程。

此练习中最大的困难是,在给定的抽象级别上,您的功能可能包含了太多无关的行为,因此您需要在较低的级别上处理其中的一些行为。您正确地认为,清晰度是维护代码的关键,但是使代码的行为清晰(当前状态)与使代码的意图清晰非常不同。

通过让它们的功能签名识别出什么,使较小的代码段的方式抽象,较大的代码段应更易于分类。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.