是否应该将较旧的代码更新为使用较新的语言构造,还是应该使用过时的构造?


15

我想在很久以前编写的某些仍能正常工作的代码中进行一些增强,然后再以其编写的编程语言具有更多功能。从理论上讲,整个项目都使用该语言的最新版本。但是,该特定模块(实际上还有许多其他模块)仍然使用较旧的方言编写。

我是不是该:

  • 不用接触我不需要接触的代码部分,而是利用新的语言功能编写我的补丁程序,这些新功能使编写补丁程序更加容易,但是在模块中其他任何地方都无法使用?(这是我直观选择的解决方案。)
  • 忽略岁月流逝的事实,并在编写补丁时反映出代码其余部分中使用的样式,就像我早在多年前一样正在做同样的事情一样吗?(我直觉上认为这种解决方案很愚蠢,但是鉴于每个人都在谈论“好的代码”而大惊小怪,因此不惜一切代价保持一致性,也许这就是我应该做的。)
  • 更新整个模块以使用较新的语言构造和约定?(这可能是最好的解决方案,但可能需要大量时间和精力,而最好将其花费在其他任务上。)

1
@JerryCoffin我不会继续在评论中争论:我在meta发布了我们可以进行适当讨论的地方。

Answers:


10

对于这个问题不可能给出确切的答案,因为它在很大程度上取决于具体情况。

代码库样式的一致性很重要,因为它有助于使代码更易于理解,这是可维护性最重要的方面之一。
您不会成为第一个因混用不同样式而使代码难以理解而被诅咒的程序员。甚至可能是您自己在几年内进行诅咒。

另一方面,使用首次编写代码时不存在的新语言构造并不意味着您会降低可维护性,甚至不会破坏代码风格。所有这些都取决于您要使用的特定语言功能以及团队对代码的旧样式和新功能的熟悉程度。

例如,通常不建议开始将功能性编程概念(例如map / reduce)引入完全采用OO风格的代码库中,但是可以在此处和此处向未使用的程序中添加一个lambda函数。像以前一样。


1
我部分不同意。我们应该遵循开闭原则。因此,我们应尽可能地隔离新代码,并同时使用可能的最新语言构造编写新代码。
阿南·瓦迪亚

3
@AnandVaidya:恕我直言,这是不切实际的期望,因为它假定旧代码遵循OCP,或者可以很容易地更改为遵循OCP,这很少发生,或者根本不值得付出努力。
布朗

1
当我说ocp时,我的意思不是oop ocp,而是ocp。基本上,这是尽量不要修改现有代码,而要隔离自己的代码。使用旧的程序代码甚至可能。我了解到,无法通过这种方式修改意大利面条式代码,但是对于任何范式中设计良好的代码,开闭应该很容易应用。可能是oops,过程还是功能。
阿南·瓦迪亚

2
@AnandVaidya:当您不表示OCP时,您不应那样称呼它。我想您真正的意思是Feather所说的用发芽方法编写新代码,因此可以将新代码样式限制为一种新的,分离的方法。如果这是可行且明智的,那么您是正确的,那很好。不幸的是,这种方法并不总是适用的,至少在您想保留旧代码DRY的情况下也是如此。
布朗

@DocBrown:我不认为开放/封闭原则仅限于面向对象的编程。您可以具有在其中添加新过程但不修改现有过程的模块,即,您可以保持现有代码的语义不变,并且,如果需要新功能,则可以编写新代码。
Giorgio

5

在很大程度上,代码及其外观无关紧要。什么代码确实是最重要的。

如果您可以保证更改/重写代码不会改变代码的作用,那么您可以继续将代码重构为您真正的兴趣所在。该“保证”是一组详尽的单元测试,可以在更改之前和之后运行,没有明显的区别。

如果您不能保证稳定性(您还没有进行这些测试),那就别管它了。

没有人会感谢您“破坏”了业务关键型软件,即使您试图使其“更好”。“工作”胜过“更好”。

当然,没有什么可以阻止您创建一组这样的测试以准备进行此类练习的...


4

几乎可以用成本和收益来分析任何事情,我认为这适用于此。

第一种选择的明显好处是,它可以在短期内最大程度地减少工作量,并通过重写工作代码来最大程度地减少破坏某些事物的机会。显而易见的代价是它将不一致性引入代码库。当您执行某项操作X时,它在代码的某些部分以一种方式完成,而在代码的不同部分以另一种方式完成。

对于第二种方法,您已经注意到了明显的好处:一致性。显而易见的代价是,您必须下定决心以可能几年前就已经放弃的方式工作,并且代码始终无法读取。

对于第三种方法,显而易见的成本就是必须要做很多工作。不太明显的代价是您可能会破坏正在运行的东西。如果(通常是)旧代码的测试不足以确保其继续正常运行,则这种情况尤其可能发生。显而易见的好处是(假设您成功完成了)您拥有了漂亮,闪亮的新代码。除了使用新的语言构造外,您还可以重构代码库,即使您仍按编写时所使用的语言使用该语言,它几乎总是会对其自身进行改进-添加新的结构来工作更轻松,这很可能是一个重大胜利。

不过,还有一个要点:目前看来,该模块在很长一段时间内维护最少(即使整个项目都得到维护)。这往往表明它编写得相当好并且相对没有错误-否则,在此期间可能需要进行更多的维护。

这就引出了另一个问题:您现在所做的更改的来源是什么?如果要修复一个总体上仍能很好满足其要求的模块中的小错误,这可能表明重构整个模块的时间和精力可能会浪费大量时间,而这需要有人去解决。再说一次,它们可能处于与您现在大致相同的位置,维护的代码不符合“现代”期望。

但是,也有可能需求发生了变化,并且您正在研究满足这些新需求的代码。在这种情况下,您的初次尝试实际上不会满足当前要求的机会很大。在需求再次稳定之前,还需要进行几轮修订的机会要大得多。这意味着您更有可能在近期内(相对)在此模块中进行重要的工作,并且更有可能在模块的其余部分进行更改,而不仅仅是在您所知的一个领域内进行更改。现在。在这种情况下,重构整个模块更有可能带来切实的近期收益,这些收益证明了额外工作的合理性。

如果需求已更改,则您可能还需要查看所涉及的更改类型以及导致更改的原因。例如,假设您正在修改Git,以将SHA-1的使用替换为SHA-256。这是要求的变化,但是范围是明确定义的并且非常狭窄。正确存储和使用SHA-256后,您就不太可能遇到影响其余代码库的其他更改。

在另一个方向上,即使开始时很小且离散,对用户界面的更改也有膨胀的趋势,因此,以“向该屏幕添加一个新复选框”开头的结果更像是:“更改此固定UI支持用户定义的模板,自定义字段,自定义配色方案等。”

对于前一个示例,最小化更改和一致性方面的错误可能最有意义。对于后者,完全重构更有可能获得回报。


1

我会为你的第一选择。当我编码时,我遵循规则以使其保持更好的状态。

因此,新代码遵循最佳实践,但是我不会接触与我正在研究的问题无关的代码。如果做得好,那么新代码应该比旧代码更具可读性和可维护性。我发现总的可维护性会变得更好,因为如果您坚持这一点,那么工作所需的代码就会越来越多地成为“更好”的代码。这样可以更快地解决问题。


1

就像许多其他事情一样,答案取决于它。如果更新较旧的结构具有主要优势,例如,与之相比,则大大改善了长期可维护性(例如避免使用回调地狱)。如果没有主要优势,保持稳定可能就是您的朋友

此外,与避免在两个单独的函数中嵌入两个样式相比,您还希望避免在同一函数中嵌入两个样式。

综上所述:您的最终决定应基于对特定案例的“成本” /“收益”分析。

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.