You need to differentiate between a builder and a factory method.
A builder is used to avoid a telescoping constructor where you create multiple constructors to enable users to create the object with different attributes. Therefore using multiple apply
methods in companion objects results in such telescoping constructors and can lead to cluttered code (anti-pattern). An example would be:
case class ComplexClass(foo: String, bar: Int)
case class ComplexClassBuilder(foo: Option[String], bar: Option[Int]){
def withFoo(f: String) =
this.copy(foo = Some(f))
def withBar(b: Int) =
this.copy(bar = Some(b))
def finish = {
// this is the place to decide what happens if there are missing values
// possible ways to handle would be to throw an exception, return an option or use defaults
ComplexClass(foo getOrElse ":D", bar getOrElse 42)
}
}
A factory method is commonly used to enable polymorphism. An apply
method is a great way of implementing a factory method
trait C
case class Foo() extends C
case class Bar() extends C
object C {
def apply(isAwesome: Boolean): C = isAwesome match {
case true => Foo()
case false => Bar()
}
}
But as always, those are patterns not rules. It is common to use one or two apply methods to allow creation of objects with different parameters. But if you need to set a lot of different values, you should avoid the apply
method and implement a builder.