使用函数返回值作为参数提供的对象,是一种好的编程实践吗?

3

我想问一下这是一个好的Java实践,还是有更好的方式(官方方式)来完成同样的事情。

首先,我需要更新一些哈希表信息:

Map<Date, Object> rows = new HashMap<Date, Object>();

这是一个用于Excel行的对象,每个日期(例如10月1日、10月2日等)和包含该行信息的对象。

因此,为了获取这些信息,我有一些方法,例如:

rows = performQuery1(rows, foo, bar, initDate, endDate);
rows = performQuery2(rows, someDAO, foo);

并且...

private HashMap<Date, Object> performQuery1(rows, foo, bar, Date, Date) {
  // Some code that adds or removes elements from the hashmap "rows"
  rows.put(date1, o1);

  //Then return the same object
  return rows;
}

所以我的问题是:这是一个好的Java实践吗?
rows = performQuery1(rows, foo, bar, initDate, endDate);
rows = performQuery2(rows, someDAO, foo);

还是不还?


4
问题过于宽泛,对此没有好的答案。然而,在这种情况下返回HashMap是完全不必要的。 - meskobalazs
所以,现在不是“rows = performQuery1(...)”,而是“performQuery1(...)”,并且performQuery1将会是“private void”,我说得对吗? - George Fandango
1
虽然不是对你问题的确切回答,但在映射中使用复杂对象作为键是一种不好的做法。 - Aleksandr M
1
请查看 https://dev59.com/hW435IYBdhLWcg3w0Trc。 - Aleksandr M
关键对象并不重要,它只是一个例子,可以是字符串或任何您想要的东西。 - George Fandango
显示剩余5条评论
2个回答

4
这个问题确实非常广泛,或者说,如果专注于“最佳实践”部分,则可能基于个人观点-但不是主要基于个人观点,因为存在这样一种模式的有效论据。
通常情况下,您会有一个获取数据的方法,并且应该将其放入目标数据结构中(也许是一个集合,或者在您的情况下是一个映射)。
对于这种方法的签名,有几个选项(大致类似于您的示例,但此模式可推广)。
第一个选项可能是:
/**
 * Computes ... something, and returns the result as a map
 */
Map<Date, Result> execute(Date d0, Date d1) { ... }

第二个可能是:
/**
 * Computes ... something, and places the results into the
 * given map
 */
void execute(Date d0, Date d1, Map<Date, Result> results)  { ... }

然而,为了最大限度的灵活性,我经常参考第三个选项(也就是你实际询问的那一个):

/**
 * Computes ... something, and places the results into the
 * given map, which is then returned. If the given map is
 * null, then a new map will be created and returned.
 */
Map<Date, Result> execute(
    Date d0, Date d1, Map<Date, Result> results)  { ... }

这有几个优点:
  • You conveniently let the call create a new map:

    Map<Date, Result> results = execute(d0, d1, null);
    
  • You can determine the implementation of the target data structure. If you always returned a new map, then there would be no way to choose between a HashMap or a LinkedHashMap, for example. Passing the target data structure to the method allows you to call

    Map<Date, Result> results = 
        execute(d0, d1, new HashMap<Date, Result>());
    

    or

    Map<Date, Result> results = 
        execute(d0, d1, new LinkedHashMap<Date, Result>());
    

    respectively

  • You don't have to create a new map for each call. For example, you could create a sequence of calls

    Map<Date, Result> results = new HashMap<Date, Result>();
    execute(d0, d1, results);
    execute(d2, d3, results);
    

    accumulating the results in the given map


当考虑到这种方法可以轻松地模拟两种替代方案时,它的强大之处可能会变得更加明显:

class DB {

    // The private method that can emulate both public methods:
    private Map<Date, Result> executeImpl(
        Date d0, Date d1, Map<Date, Result> results);

    // The implementation that returns a new map
    public Map<Date, Result> execute(Date d0, Date d1) {
        return executeImpl(d0, d1, null);
    }

    // The implementation that fills a given map
    public void execute(Date d0, Date d1, Map<Date, Result> results) {
        executeImpl(d0, d1, results);
    }

}

一个旁注:Java SDK 的某些地方也使用了类似的模式。例如,在另一个应用案例中:BufferedImageOp#filter

BufferedImage filter(BufferedImage src, BufferedImage dest)

... 如果目标图像为 null,则创建具有适当 ColorModel 的 BufferedImage。

返回:过滤后的 BufferedImage


哇!!!非常感谢你,Marco! - George Fandango
1
答案很好,绝对比我的好 :-) 我只想补充一点,你的组合选项在我看来有一个缺点,就是稍微有些反直觉,需要阅读文档才能正确地阅读和使用 - 在许多情况下,这可能会抵消它的优点。 - Martin Modrák

1

这个问题的核心在于Java是按引用传递还是按值传递?

此外,您应该返回一个新创建的对象或修改参数引用的对象,但不能同时进行。有一些有用的例外情况,例如方法链式调用,但这似乎不是这种情况。

当您从方法返回数据时,程序员会期望数据是一个新创建的对象,而通过参数引用的对象保持不变。使方法返回void(或成功代码)提示程序员该方法修改了由参数引用的对象而不是返回新对象,使您的代码更易于阅读。


我认为Java不通过引用传递参数。 - George Fandango
@AleksandrM 感谢你指出我在回答中忽略的区别。我在我的回答中链接了这个问题。我仍然认为回答中重要的部分(你应该将引用作为参数传递或返回新对象)是正确的。如果不是这样,请告诉我。 - Martin Modrák

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接