总是避免Java中的输入输出参数?

问题描述:

毫无疑问,输入输出参数会导致代码混淆,因为它们可能会增加意外/不可预测的副作用。总是避免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原则,认为具有返回类型且没有副作用的具有无效返回潜在(但允许,因为它是约定)副作用的“命令”方法和“查询”方法。

那么,一个激励程序员应该采用什么?两种符合代码情况的混合?或者保持“法律”期望始终避免输入参数...

(当然,添加元素的方法是为了解释示例而命名的,并且在实际代码中是错误的名称选择)。

我认为法律应该是:

用的是更直接的,但总是总是文档你的方法广泛的行为。

你的第二个例子是一个非常好的情况下没有证件,你将有保证的缺陷:该方法的名称为addElementsToExistingList,但不元素添加到现有列表的方法 - 它创建一个新的。一个反直觉和误导性的名字,至少可以说...

+0

我非常同意你的看法,但它代表了一些非常受欢迎的方法(非实验总是这样做):)事实上,我读了很多类似的方法, – Mik378 2012-03-12 19:54:55

在你的例子中,名称明确表示 - “addElementsToExistingList”对我来说似乎很清楚,暗示你将要.. er .. 你懂。但是你的担心可以用一个不太明显的名字来证明。

例如,在红宝石这通常与命名约定

"a".upcase =>为您提供了变量的大写,离开原来的不变 "a".upcase! =>改变处理的原始变量

+1

除了一点细节:*提示*在第二个片段中完全错误,因为现有列表未被修改:-) – thkala 2012-03-12 20:09:32

它的优良只要有文件记载,就可以改变/改变参数。当然,使用方法名称“addElementsToExistingList”,还应该指望什么?然而,正如有人以前指出的,你的第二个实现返回一个副本,并且不会修改原始的,所以方法名现在是误导性的。你的第一个方法是完全可以接受的做事方式。唯一的其他改进是如果仅将所有元素添加到列表中,则可能向返回值添加true/false值以指示true。

还有第三种方法。裹List<String>到知道如何将元素添加到自己的类:

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

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

我喜欢这种方法,因为它使List实现私有。如果有人将不可变列表传递给您的方法或参数是否已修改,则不必担心。代码更简单。像addElementsToExistingList这样的长方法名称是代码的气味,对象正在尝试做另一个对象应该做的事情。

当变异作为参数的对象时,您应该始终记录文档,否则这可能会给调用者带来意想不到的副作用。在第一种情况下,我同意其他评论说方法名称是足够的文档。

在你的第二个例子中,已经出现在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()的客户端现在还可以使用元素列表完成其他事情。