在现有系统中添加新功能时,如果您遇到几乎所需的现有功能,那么最佳做法是:
-OR -
答案 0 :(得分:3)
你不能总是这样做,但这里的一个解决方案是将你现有的功能分成其他很小的部分,允许你使用你需要的部分而不需要编辑所有的代码,并且更容易编辑小部分代码。
话虽如此,如果你认为你可以在系统的现有部分引入新的错误而不注意它,你应该考虑使用单元测试。
答案 1 :(得分:2)
我倾向于遵循的经验法则是,如果我可以通过向现有函数添加额外参数(或新的有效值)来覆盖新行为,同时使代码或多或少“明显相同”在现有案例中,改变一个函数没有太大的危险。
例如,旧代码:
def utf8len(s):
return len(s.encode('utf8')) # or maybe something more memory-efficient
新用例 - 我正在使用空对象模式的样式编写一些代码,因此我希望utf8len(None)
返回None
而不是抛出异常。我可以定义一个新函数utf8len_nullobjectpattern
,但这会很快变得非常烦人,所以:
def utf8len(s):
if s != None:
return len(s.encode('utf8')) # old code path is untouched
else:
return None # new code path introduced
然后,即使utf8len
的单元测试不完整,我也可以打赌我没有更改None
以外的任何输入的行为。我还需要检查没有人依赖utf8len
为None
输入抛出异常,这是(1)文档质量和/或测试的问题; (2)人们是否真正关注定义的接口,或者只是使用源。如果是后者,我需要查看调用网站,但如果事情做得好,那么我几乎不会。
旧的允许输入是否仍处理“显然是相同的”并不是一个问题,即修改代码的百分比,它是如何被修改的。我已经选择了一个故意琐碎的例子,因为整个旧的函数体在新函数中显然仍然存在,但我认为当你看到它时你会知道它。另一个例子是使用一个只提供旧固定值的默认参数来制作固定可配置的东西(可能通过传递值或用于获取值的依赖项)。旧固定事物的每个实例都被替换为(调用)新参数,因此在差异上看到变化意味着什么是相当容易的。你有(或写)至少一些测试,以确保你没有通过一些愚蠢的错字打破旧的输入,所以你可以继续前进,甚至没有完全放心你的测试覆盖率。
当然,您需要全面的测试,但您不一定拥有它。这里还有两个相互竞争的维护要求:1 - 不重复代码,因为如果它有错误,或者将来可能需要更改的行为,那么你就会复制错误/当前行为。 2 - 开放/封闭原则,有点高,但基本上说,“写出有效的东西,然后不要碰它”。 1说你应该重构在这两个类似的操作之间共享代码,2说不,你已经发运了旧的,或者它可以用于这个新的东西,或者它不是,如果不是,那么不管它
答案 2 :(得分:1)
您应始终努力避免代码重复。因此,我建议您尝试编写一个新函数,修改现有函数的返回值以实现新功能。
我确实意识到在某些情况下可能无法做到这一点。在这些情况下,您绝对应该考虑重写现有函数而不更改其接口。并且通过应该来引入新的错误,可以通过在修改后的函数上运行的单元测试来阻止将添加到项目代码中。
如果您只需要部分现有函数,请考虑从现有函数中提取一个新函数,并在现有函数和新函数中使用这个新的“辅助函数”。再次通过单元测试确认一切正常。