2011-10-25 29 views
3

当添加新的功能,现有的系统,如果你遇到一个现有的功能几乎做你需要的是它最好的做法是:重用现有的功能

  • 复制现有的功能,并进行更改在新的副本上(知道复制代码会让你的同伴哭泣)。

- 或 -

  • 编辑现有的函数来处理双方现有的情况和新的情况下,冒险,你可能会引入新的问题到系统的现有部分(这让QA团队哭)
    • 如果要修改你在哪里划清界线你应该创建一个新的独立的功能(基于复印件)......功能的10%,该函数的50%之前,现有的功能?
+2

如果该函数足够复杂以至于只能修改该函数的10%,则无论如何都需要对其进行重构。 –

+1

理想的是,您的测试足够好,您可以放心地修改现有功能而不破坏它。所以事实上,甚至有一个问题可以在这里问预先假设测试不够好,我认为答案将取决于它有多糟糕,从“我们测试,我们只是不没有时间去做我们想做的一切事情“,”令人震惊“。 –

回答

2

经验法则我倾向于遵循的是,如果我可以通过向现有函数添加一个额外参数(或新的有效值)来覆盖新行为,同时使代码或多或少地“明显相同”在现有的情况下,那么在改变功能方面没有太大的危险。

例如,旧代码:

def utf8len(s): 
    return len(s.encode('utf8')) # or maybe something more memory-efficient 

新Use Case - 我正在写在使用空对象模式的样式一些代码,所以我想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说不,你已经发运了旧的,或者它可以用于这个新事物,或者它不是,如果不是,那么让它独自一人。

1

你应该总是努力避免重复代码。因此,我建议您尝试编写一个新函数来修改已有函数的返回值以实现您的新功能。

我知道在某些情况下可能无法做到这一点。在这些情况下,您绝对应该考虑重写现有的功能而不更改其接口。并通过这样做应该通过,可以在修改后的功能运行单元测试,你将它添加到项目代码之前阻止引入新的bug。

如果你只需要对现有功能的一部分,可以考虑从现有的提取新的功能,并在现有的和新的功能,使用这个新的“帮手”功能。通过单元测试再次确认一切正常。这里

3

你不能总是这样做,但一个解决办法是在其他小零件拆分现有的功能,让您的使用,需要在不修改所有的代码的部分,使其更容易编辑小块的代码。

话虽这么说,如果你认为你可以引入新的问题到系统的现有部分没有注意到它,你应该考虑使用单位的测试。