将许多参数传递给方法的最佳实践?

Saw*_*yer 81 java parameters performance parameter-passing

偶尔,我们必须编写接收许多参数的方法,例如:

public void doSomething(Object objA , Object objectB ,Date date1 ,Date date2 ,String str1 ,String str2 )
{
}
Run Code Online (Sandbox Code Playgroud)

当我遇到这种问题时,我经常将参数封装到地图中.

Map<Object,Object> params = new HashMap<Object,Object>();
params.put("objA",ObjA) ;

......

public void doSomething(Map<Object,Object> params)
{
 // extracting params 
 Object objA = (Object)params.get("objA");
 ......
 }
Run Code Online (Sandbox Code Playgroud)

这不是一个好的做法,将params封装到地图中完全是浪费效率.好处是,干净的签名,容易添加其他params与最少的修改.这种问题的最佳做法是什么?

JRL*_*JRL 122

Effective Java,第7章(方法),第40项(仔细设计方法签名)中,Bloch写道:

有三种技术可以缩短过长的参数列表:

  • 将方法分解为多个方法,每个方法只需要一部分参数
  • 创建辅助类来保存参数组(通常是静态成员类)
  • 使Builder模式从对象构造适应方法调用.

有关详细信息,我鼓励您购买该书,这非常值得.

  • @jtate 是个人选择还是您遵循官方文档? (3认同)
  • @RedM 根据 Rober Martin 在他的书 Clean Code 中的说法,方法应该是 Monadic(只有一个参数),因为多个参数很难理解。他还指出,如果您需要传递多个参数,最好创建一个包装类并传递该对象。 (3认同)
  • 在《Effective Java》第三版中,这是第 8 章(方法)第 51 项 (3认同)
  • @RedM我一直认为超过3或4个参数"过长" (2认同)
  • @RedM 个人喜好:) (2认同)

tom*_*tom 68

使用带有神奇字符串键的地图是个坏主意.您丢失了任何编译时检查,并且实际上不清楚所需的参数是什么.您需要编写非常完整的文档来弥补它.你会记得几周内这些字符串是什么而不看代码吗?怎么打错了怎么办?使用错误的类型?在运行代码之前,您不会发现.

而是使用模型.创建一个类,它将成为所有这些参数的容器.这样你就可以保持Java的类型安全.您还可以将该对象传递给其他方法,将其放入集合中等.

当然,如果参数集没有在别处使用或传递,专用模型可能过度.要达到平衡,所以要运用常识.


Ha.*_*Ha. 24

如果您有许多可选参数,则可以创建流畅的API:将单个方法替换为方法链

exportWithParams().datesBetween(date1,date2)
                  .format("xml")
                  .columns("id","name","phone")
                  .table("angry_robots")
                  .invoke();
Run Code Online (Sandbox Code Playgroud)

使用静态导入,您可以创建内部流畅的API:

... .datesBetween(from(date1).to(date2)) ...
Run Code Online (Sandbox Code Playgroud)

  • 如果每个参数都是必需的,而不是可选的怎么办? (3认同)
  • 对于非常有创意的想法+1,我不得不承认! (2认同)

dim*_*rvp 12

它被称为"引入参数对象".如果您发现自己在几个地方传递相同的参数列表,只需创建一个包含所有参数列表的类.

XXXParameter param = new XXXParameter(objA, objB, date1, date2, str1, str2);
// ...
doSomething(param);
Run Code Online (Sandbox Code Playgroud)

即使你没有发现自己经常传递相同的参数列表,这种简单的重构仍然会提高你的代码可读性,这总是好的.如果您在3个月后查看代码,则在需要修复错误或添加功能时更容易理解.

这当然是一般的哲学,既然你没有提供任何细节,我也不能给你更详细的建议.:-)


tva*_*son 10

首先,我尝试重构该方法.如果它使用那么多参数,那么任何方式都可能太长.将其分解将改进代码并可能减少每个方法的参数数量.您也可以将整个操作重构为自己的类.其次,我会寻找其他实例,我使用相同参数列表的相同(或超集).如果您有多个实例,则可能表示这些属性属于一起.在这种情况下,创建一个类来保存参数并使用它.最后,我将评估参数的数量是否值得创建一个地图对象以提高代码的可读性.我认为这是个人电话 - 这种解决方案各方面都有痛苦,而且权衡点可能会有所不同.对于六个参数,我可能不会这样做.对于10我可能会(如果没有其他方法首先工作).


Boh*_*dan 7

在构造对象时,这通常是一个问题.

在这种情况下,使用构建器对象模式,如果您有大量参数并且并不总是需要所有参数,则它可以很好地工作.

您还可以将其调整为方法调用.

它还大大提高了可读性.

public class BigObject
{
  // public getters
  // private setters

  public static class Buider
  {
     private A f1;
     private B f2;
     private C f3;
     private D f4;
     private E f5;

     public Buider setField1(A f1) { this.f1 = f1; return this; }
     public Buider setField2(B f2) { this.f2 = f2; return this; }
     public Buider setField3(C f3) { this.f3 = f3; return this; }
     public Buider setField4(D f4) { this.f4 = f4; return this; }
     public Buider setField5(E f5) { this.f5 = f5; return this; }

    public BigObject build()
    {
      BigObject result = new BigObject();
      result.setField1(f1);
      result.setField2(f2);
      result.setField3(f3);
      result.setField4(f4);
      result.setField5(f5);
      return result;
    }
  }
}

// Usage:
BigObject boo = new BigObject.Builder()
  .setField1(/* whatever */)
  .setField2(/* whatever */)
  .setField3(/* whatever */)
  .setField4(/* whatever */)
  .setField5(/* whatever */)
  .build();
Run Code Online (Sandbox Code Playgroud)

您还可以将验证逻辑放入Builder set ..()和build()方法中.


Pad*_*rag 6

有一种称为参数对象的模式.

想法是使用一个对象代替所有参数.现在,即使您以后需要添加参数,也只需将其添加到对象中即可.方法界面保持不变.


Joh*_*lph 5

您可以创建一个类来保存该数据.虽然需要足够有意义,但比使用地图(OMG)要好得多.