构造函数参数 – 经验法则

通常,类构造函数应该接受的参数的最大数量是多少? 我正在开发一个需要大量初始化数据的类(目前有10个参数)。 但是,具有10个参数的构造函数感觉不对。 这让我相信我应该为每个数据创建一个getter / setter。 不幸的是,getter / setter模式不会强制用户输入数据,如果没有它,对象的表征就不完整,因此无用。 思考?

有了这么多参数,就该考虑构建模式了 。 使用build()方法创建一个包含所有这些getter和setter的构建器类,该方法返回您正在尝试构造的类的对象。

例:

public class ReallyComplicatedClass { private int int1; private int int2; private String str1; private String str2; // ... and so on // Note that the constructor is private private ReallyComplicatedClass(Builder builder) { // set all those variables from the builder } public static class Builder { private int int1; private int int2; private String str1; private String str2; // and so on public Builder(/* required parameters here */) { // set required parameters } public Builder int1(int newInt) { int1 = newInt; return this; } // ... setters for all optional parameters, all returning 'this' public ReallyComplicatedClass build() { return new ReallyComplicatedClass(this); } } } 

在您的客户端代码中:

 ReallyComplicatedClass c = new ReallyComplicatedClass.Builder() .int1(myInt1) .str2(myStr2) .build(); 

请参阅Effective Java Reloaded [pdf]的第7-9页,Josh Bloch在JavaOne 2007上的演示文稿。(这也是Effective Java 2nd Edition中的第2项 ,但我没有它,因此我无法引用它。)

您可以决定何时足够,并为构造函数(或任何其他方法)使用Introduce Parameter Object 。

代码完成2建议对任何方法有相当合理的七个参数限制。

尝试为某些成员建立合理的默认值。 这将让您使用getter / setter而不必担心表征不完整。

我认为你不能说合适的数字是“七,不多”或“五”。

构造函数的一个好的经验法则是将对象传递给它的身份 ,而不是它的状态 。 传入的参数是对象存在必不可少的参数,如果没有这些参数,对象的大多数操作都可能无法实现。

如果你真的有一个具有非常复杂的自然身份的类,因此需要许多参数,那么考虑你的类的设计。

坏构造函数的一个例子是:

 public NightWatchman(int currentFloor, int salary, int hapiness) {...} 

在这里,NightWatchman正在构建一些默认值,几乎肯定会在短时间内发生变化。 看似有趣的是,对象以一种方式被告知其价值,然后将来以不同的方式(通过他们的制定者)将它们存在。

更好的构造函数的示例是:

 public GateWatchman(Gate watchedGate, boolean shootOnSight) {...} 

守望者正在观看的大门是一个存在的信息。 在课堂上我会把它标记为私人决赛 。 我选择将shootOnSight变量传递给构造函数,因为重要的是,对象始终知道是否要射杀窃贼。 在这里,身份被用作类型。

我可以有一个名为ShootingGateWatchmanPoliceCallingGateWatchman的类 – 即参数被解释为对象标识的一部分。

我建议找到参数之间的依赖关系,然后创建结构或其他类来保存它们并将它们传递给你的构造函数,而不是乍一看似乎没有相关的一堆东西。

我需要更多地了解该类的作用以及参数是什么,但是该类可能有太多的责任。 是否可以将类拆分为较小的独立类?

使用setter不能解决具有许多依赖性/参数的类的问题。 它只是将问题移到另一个地方,并不强制输入参数。

对于方法,我尝试遵循清洁代码书中的建议,每个方法(IIRC)的参数不超过3个。 对于构造函数,我可能有更多参数,因为通常构造函数将由我的dependency injection框架调用而不是由我调用。

mmyers提到的构建器模式在构建复杂对象时也是一个很好的解决方案,并且没有办法使它们变得不那么复杂。

从短期记忆的7 +/- 2规则 ,以及对程序员注意力跨度的一些悲观情绪,我通常说不超过5。 注意:我将varargs列表视为一个实体。

如果你真的被限制一次构造对象,你通常可以在简单值对象中收集相关参数并将它们传递给构造函数。 尝试确保值对象具有一定的概念意义,而不仅仅是随机的信息集合……

这取决于。

如果某些参数属于同一类型并且可以混淆,我会容忍一个相当小的数字(例如5)。

如果参数是不同类型的,那么它们不能混淆,那么我会容忍更多。 不过,十分接近极限。

如何将Param => Value Map对象传递给构造函数? 如果调用者省略了任何关键参数,请让构造函数抛出exception。

这意味着对构造函数的错误调用只会在运行时而不是编译时捕获,这是一个缺点。 但getter / setter方法有同样的问题,这应该更容易使用。