问题

我有一些大的(超过3个字段)对象,它们可以而且应该是不可变的。每次遇到这种情况时,我倾向于使用长参数列表创建构造函数可恶。感觉不对,难以使用,可读性受损。

如果字段是某种类似列表的集合类型则更糟糕。 simpleaddSibling(S s)可以轻松创建对象,但渲染对象是可变的。

在这种情况下你们有什么用?我在使用Scala和Java,但我认为只要语言是面向对象的,问题就是语言无关。

我能想到的解决方案:

  • "具有长参数列表的构造函数可憎"
  • Builder模式

感谢你的输入!


#1 热门回答(75 赞)

那么,你想要一个创建后更容易阅读和不可变的对象?

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

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

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

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

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

FooFactory.create(),whereXXX(..)和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 热门回答(60 赞)

在Scala 2.8中,你可以在案例类中使用命名和默认参数以及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))

#3 热门回答(20 赞)

好吧,在Scala 2.8上考虑一下:

case class Person(name: String, 
                  married: Boolean = false, 
                  espouse: Option[String] = None, 
                  children: Set[String] = Set.empty) {
  def marriedTo(whom: String) = this.copy(married = true, espouse = Some(whom))
  def addChild(whom: String) = this.copy(children = children + whom)
}

scala> Person("Joseph").marriedTo("Mary").addChild("Jesus")
res1: Person = Person(Joseph,true,Some(Mary),Set(Jesus))

当然,这确实有一些问题。例如,尝试制作espouseOption[Person],然后让两个人相互结婚。如果不诉诸aprivate var和/或aprivate构造函数加工厂,我想不出解决问题的方法。


原文链接