构建大型、不可变的对象,而无需使用具有长参数列表的构造函数

2022-08-31 10:43:11

我有一些大(超过3个字段)对象,可以并且应该是不可变的。每次遇到这种情况时,我都倾向于使用长参数列表创建可憎的构造函数。

它感觉不对,很难使用,可读性也会受到影响。

如果字段是某种集合类型(如列表),则情况会更糟。一个简单的方法可以大大简化对象的创建,但会使对象可变。addSibling(S s)

在这种情况下,你们会用什么?

我使用的是Scala和Java,但我认为只要语言是面向对象的,问题就是语言不可知的。

我能想到的解决方案:

  1. “构造函数可憎的长参数列表”
  2. 生成器模式

答案 1

那么,一旦创建,您希望同时具有更易于阅读且不可变的对象吗?

我认为一个流畅的界面正确完成会帮助你。

它看起来像这样(纯粹是编造的例子):

final Foo immutable = FooFactory.create()
    .whereRangeConstraintsAre(100,300)
    .withColor(Color.BLUE)
    .withArea(234)
    .withInterspacing(12)
    .build();

我用粗体写了“正确完成”,因为大多数Java程序员都弄错了流畅的接口,并用构建对象所需的方法污染了他们的对象,这当然是完全错误的。

诀窍是只有build()方法实际上创建了一个Foo(因此Foo可以是不可变的)。

FooFactory.create()其中XXX(..)withXXX(..)都创建了“其他东西”。

其他的东西可能是FooFactory,这里有一种方法可以做到这一点....

You FooFactory看起来像这样:

// Notice the private FooFactory constructor
private FooFactory() {
}

public static FooFactory create() {
    return new FooFactory();
}

public FooFactory withColor( final Color col ) {
    this.color = color;
    return this;
}

public Foo build() {
    return new FooImpl( color, and, all, the, other, parameters, go, here );
}

答案 2

在 Scala 2.8 中,您可以在 case 类上使用命名参数和默认参数以及方法。下面是一些示例代码:copy

case class Person(name: String, age: Int, children: List[Person] = List()) {
  def addChild(p: Person) = copy(children = p :: this.children)
}

val parent = Person(name = "Bob", age = 55)
  .addChild(Person("Lisa", 23))
  .addChild(Person("Peter", 16))