2012-03-12 61 views
3

毫無疑問,輸入輸出參數會導致代碼混淆,因爲它們可能會增加意外/不可預測的副作用。總是避免Java中的輸入輸出參數?

所以,很多優秀的程序員說:

避免在出參數變化的可變方法的參數。傾向於保持參數不變。

對於期望他的代碼是最乾淨和可理解的完美主義程序員來說,這個「規則」是否必須適用於所有情況?

例如,假設一個基本的方法將元素添加到一個簡單的列表,有兩種方式:

第一種方式(與IN-OUT參數):

private void addElementsToExistingList(List<String> myList){ 
    myList.add("Foo"); 
    myList.add("Bar"); 
} 

,主叫方是:

List<String> myList = new ArrayList<String>(); 
//.......Several Instructions (or not) ..... 
addElementsToExistingList(myList); 

沒有out參數方式二:

private List<String> addElementsToExistingList(List<String> originalList){ 
     List<String> filledList = new ArrayList<String>(originalList); //add existing elements 
     filledList.add("Foo"); 
     filledList.add("Bar"); 
     return filledList; 
    } 

和呼叫者的存在:

的第二方式
List<String> myList = new ArrayList<String>(); 
//.......Several Instructions (or not) ..... 
myList.addAll(addElementsToExistingList(myList)); 

優點:

參數不被修改=>無的意想不到的副作用爲一個新的代碼讀取器的危險。

缺點的第二種方式:

非常詳細,非常不易閱讀...

當然,你會告訴我,因爲這樣做有一個簡單代碼,第一種方式是真的方便多了。但是,如果我們不考慮任何概念/代碼的難度,我會對任何讀者(不管是否是初學者)的第二種方式更合乎邏輯和明顯。

但是,它違反了CQS原則,認爲具有返回類型且沒有副作用的具有無效返回潛在(但允許,因爲它是約定)副作用的「命令」方法和「查詢」方法。

那麼,一個激勵程序員應該採用什麼?兩種符合代碼情況的混合?或者保持「法律」期望始終避免輸入參數...

(當然,添加元素的方法是爲了解釋示例而命名的,並且在實際代碼中是錯誤的名稱選擇)。

回答

5

我認爲法律應該是:

用的是更直接的,但總是總是文檔你的方法廣泛的行爲。

你的第二個例子是一個非常好的情況下沒有證件,你將有保證的缺陷:該方法的名稱爲addElementsToExistingList,但不元素添加到現有列表的方法 - 它創建一個新的。一個反直覺和誤導性的名字,至少可以說...

+0

我非常同意你的看法,但它代表了一些非常受歡迎的方法(非實驗總是這樣做):)事實上,我讀了很多類似的方法, – Mik378 2012-03-12 19:54:55

0

在你的例子中,名稱明確表示 - 「addElementsToExistingList」對我來說似乎很清楚,暗示你將要.. er .. 你懂。但是你的擔心可以用一個不太明顯的名字來證明。

例如,在紅寶石這通常與命名約定

"a".upcase =>爲您提供了變量的大寫,離開原來的不變 "a".upcase! =>改變處理的原始變量

+1

除了一點細節:*提示*在第二個片段中完全錯誤,因爲現有列表未被修改:-) – thkala 2012-03-12 20:09:32

0

它的優良只要有文件記載,就可以改變/改變參數。當然,使用方法名稱「addElementsToExistingList」,還應該指望什麼?然而,正如有人以前指出的,你的第二個實現返回一個副本,並且不會修改原始的,所以方法名現在是誤導性的。你的第一個方法是完全可以接受的做事方式。唯一的其他改進是如果僅將所有元素添加到列表中,則可能向返回值添加true/false值以指示true。

2

還有第三種方法。裹List<String>到知道如何將元素添加到自己的類:

class ElementList { 
    private List<String> = new ArrayList<String>(); 

    public void addElements(Element... elements); 
} 

我喜歡這種方法,因爲它使List實現私有。如果有人將不可變列表傳遞給您的方法或參數是否已修改,則不必擔心。代碼更簡單。像addElementsToExistingList這樣的長方法名稱是代碼的氣味,對象正在嘗試做另一個對象應該做的事情。

2

當變異作爲參數的對象時,您應該始終記錄文檔,否則這可能會給調用者帶來意想不到的副作用。在第一種情況下,我同意其他評論說方法名稱是足夠的文檔。

在你的第二個例子中,已經出現在myList中的元素似乎被添加了兩次。事實上,你可以完全移除addElementsToExistingList方法的參數,它改寫爲:

private List<String> getElements() { 
    List<String> filledList = new ArrayList<String>(); 
    filledList.add("Foo"); 
    filledList.add("Bar"); 
    return filledList; 
} 

List<String> myList = new ArrayList<String>(); 
//.......Several Instructions (or not) ..... 
myList.addAll(getElements()); 

注意,該代碼不等同於你的第二個例子,因爲該元素只加一次,但我認爲這實際上是什麼你打算。這是我通常喜歡的風格。此代碼比第一個示例更容易理解並且更靈活,無需添加額外的代碼(它可能會使性能略有下降,但這通常不是問題)。除了將元素列表添加到現有集合之外,getElements()的客戶端現在還可以使用元素列表完成其他事情。