2017-05-28 33 views
0

我有一個簡單的複製/ clone方法那就是我的應用程序重要:如何在添加新字段時避免破解克隆/複製方法?

@Override 
    public Operation getCopy() { 
    Operation copy = new Operation(); 
    copy.year = this.year; 
    copy.stage = this.stage; 
    copy.info = this.info; 
    copy.user = this.user.getCopy(); 
    // NOT TO BE COPIED! copy.id = this.id; 
    ... 
    return copy; 
} 

注意,有一些不應該被複制了一些具體領域。還有一些複雜的對象(如用戶)有自己的複製方法。

的問題是,由於新代碼開發的,有時開發人員創建應該複製一個新的領域,但他忘了將它添加到copy方法:

private String additionalInfo; 

而且即使沒有一個編譯錯誤,這是一個業務問題,只有我們的QA團隊甚至用戶纔會發現。

我能做些什麼來防止這種情況發生?我已經嘗試過使用原始對象和其副本進行比較的JUnit測試,並且它們適用於現有字段,但它們不佔用新字段。

回答

2

我用我稱之爲「環和開關」測試此:

for (Field field : Operation.class.getFields()) { 
    switch (field.getName()) { 
    case "year": 
     // Test that year is copied correctly. 
     // Initialize blah so that year is set. 
     assertEquals(getCopy(blah).year, blah.year); 
     break; 
    case "stage": 
     // Test that stage is copied correctly. 
     // Initialize blah so that stage is set. 
     assertEquals(getCopy(blah).stage, blah.stage); 
     break; 
    case "id": 
     // We don't want to copy id. 
     // Initialize blah so that id is set. 
     assertNull(getCopy(blah).id); 
     break; 

    // etc. 

    default: 
     throw new AssertionError("Unhandled field: " + field.getName()); 
    } 
} 

這不是一個非常有想象力的名字:您遍歷所有的類的字段,然後切換直線距離,以便您可以分別明確地處理各個字段。

這樣做的好處是,default案件立即發現缺乏對新增字段的處理。在測試中你會得到一個很棒的大耳光,說你需要在測試中處理它 - 並且擴展而言,你也需要在生產代碼中處理它。

使用普通的舊Java反射時的缺點是它不會捕獲被刪除的字段。這可能是一個「不太糟糕」的情況,因爲它只是你剩下的未使用的代碼,而不是生產代碼中未經測試的代碼路徑。


我開發(或讀取某處,我不幸無法回憶)此成語,同時建立協議緩衝區協議緩衝區轉換器。 Java的協議緩衝區有generated field numbers,所以實際上你可以打開的場數,而不是名稱:

for (FieldDescriptor fieldDesc : proto.getDescriptorForType().getFields()) { 
    switch (fieldDesc.getNumber()) { 
    case FIELD1_FIELD_NUMBER: 
     // ... 
    case FIELD2_FIELD_NUMBER: 
     // ... 
    } 
} 

關於這樣做的好處是,你瞭解刪除的情況下也是如此,因爲場數將不再生成,這意味着測試開關將不再編譯。

+0

面對變化,這種方法極其不穩定。測試'assertEquals(複製,原始,「複製失敗」);'通常就足夠了。 –

+0

@LewBloch,除了依賴equals檢查所有的字段。 –

+0

只有那些需要建立平等的人。如果這個狀態是由其他領域決定的,那麼他們應該用「等」來解釋。 –

0

代碼審查如何錯過缺少重寫?

此外,複製方法不應該太脆弱。如果您有「不應複製的特定字段」,爲什麼它們對子類可見?

爲什麼繼承層次如此之深?如果每個需要複製的類型都實現一個接口,那麼在開發過程中錯過重寫會更加困難,並且您不需要深層繼承層次結構。那些碰巧繼承getCopy()的合適的基本實現的類可以通過super.來調用它以開始,那些不簡單地實現繼承的接口方法的類。

您不能強制程序員通過編譯器從具體實現中覆蓋方法。代碼審查應該會發現這樣的錯誤。如果他們沒有對那些錯過評論的人說一句話。

抽象方法的實現更容易被捕獲,因爲如果你不這樣做,編譯器會發出呻吟聲。

相關問題