2011-10-25 31 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

你不能總是這樣做,但一個解決辦法是在其他小零件拆分現有的功能,讓您的使用,需要在不修改所有的代碼的部分,使其更容易編輯小塊的代碼。

話雖這麼說,如果你認爲你可以引入新的問題到系統的現有部分沒有注意到它,你應該考慮使用單位的測試。