Is there a better builder pattern in scala companion object apply than Companion.forBla.withBla.withBla?

StackOverflow https://stackoverflow.com/questions/20630147

  •  18-09-2022
  •  | 
  •  

Question

What If I have multiple ways to create an object, should I be using multiple apply methods in my companion object? it would make it ugly to have multiple apply methods I prefer a single one. But if I have multiple ways to construct my object that would mean I'm back to the standard builder pattern as i used in java. (Companion.forBla.withBla.withBla)

Was it helpful?

Solution

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.

OTHER TIPS

Your question sounds like a matter of opinion or taste. (Why do you think having multiple apply methods is ugly?).

Method parameters can have default values, and parameters can be named:

class MySomething(val one: String, val two: Int)

object MySomething {
  def apply(one: String = "hello", two: Int = 0) = new MySomething(one, two)
}

// Creates a MySomething with one = "hello" and two = 0
val a = MySomething()

// Creates a MySomething with one = "bye" and two = 0
val b = MySomething("bye")

// Creates a MySomething with one = "hello" and two = 2
val c = MySomething(two = 2)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top