Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the Scala equivalent to a Java builder pattern?

In the work that I do on a day to day in Java, I use builders quite a lot for fluent interfaces, e.g.: new PizzaBuilder(Size.Large).onTopOf(Base.Cheesy).with(Ingredient.Ham).build();

With a quick-and-dirty Java approach, each method call mutates the builder instance and returns this. Immutably, it involves more typing, cloning the builder first before modifying it. The build method eventually does the heavy lifting over the builder state.

What's a nice way of achieving the same in Scala?

If I wanted to ensure that onTopOf(base:Base) was called only once, and then subsequently only with(ingredient:Ingredient) and build():Pizza could be called, a-la a directed builder, how would I go about approaching this?

like image 475
Jakub Korab Avatar asked Jan 07 '11 12:01

Jakub Korab


People also ask

What is builder pattern in Scala?

Builder pattern lets you construct a complex object in a step by step fashion so that you can create various representations of the object through the same construction code. Builder pattern uses the builder class to make initialization as a series of steps to execute.

What is builder pattern in Java?

Builder pattern solves the issue with large number of optional parameters and inconsistent state by providing a way to build the object step-by-step and provide a method that will actually return the final Object.

Which type of design pattern is builder pattern?

Builder pattern builds a complex object using simple objects and using a step by step approach. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create an object. A Builder class builds the final object step by step.


2 Answers

You have three main alternatives here.

  1. Use the same pattern as in Java, classes and all.

  2. Use named and default arguments and a copy method. Case classes already provide this for you, but here's an example that is not a case class, just so you can understand it better.

    object Size {     sealed abstract class Type     object Large extends Type }  object Base {     sealed abstract class Type     object Cheesy extends Type }  object Ingredient {     sealed abstract class Type     object Ham extends Type }  class Pizza(size: Size.Type,              base: Base.Type,              ingredients: List[Ingredient.Type])  class PizzaBuilder(size: Size.Type,                     base: Base.Type = null,                     ingredients: List[Ingredient.Type] = Nil) {      // A generic copy method     def copy(size: Size.Type = this.size,              base: Base.Type = this.base,              ingredients: List[Ingredient.Type] = this.ingredients) =          new PizzaBuilder(size, base, ingredients)       // An onTopOf method based on copy     def onTopOf(base: Base.Type) = copy(base = base)       // A with method based on copy, with `` because with is a keyword in Scala     def `with`(ingredient: Ingredient.Type) = copy(ingredients = ingredient :: ingredients)       // A build method to create the Pizza     def build() = {         if (size == null || base == null || ingredients == Nil) error("Missing stuff")         else new Pizza(size, base, ingredients)     } }  // Possible ways of using it: new PizzaBuilder(Size.Large).onTopOf(Base.Cheesy).`with`(Ingredient.Ham).build(); // or new PizzaBuilder(Size.Large).copy(base = Base.Cheesy).copy(ingredients = List(Ingredient.Ham)).build() // or new PizzaBuilder(size = Size.Large,                   base = Base.Cheesy,                   ingredients = Ingredient.Ham :: Nil).build() // or even forgo the Builder altogether and just  // use named and default parameters on Pizza itself 
  3. Use a type safe builder pattern. The best introduction I know of is this blog, which also contains references to many other articles on the subject.

    Basically, a type safe builder pattern guarantees at compile time that all required components are provided. One can even guarantee mutual exclusion of options or arity. The cost is the complexity of the builder code, but...

like image 38
Daniel C. Sobral Avatar answered Oct 14 '22 13:10

Daniel C. Sobral


Another alternative to the Builder pattern in Scala 2.8 is to use immutable case classes with default arguments and named parameters. Its a little different but the effect is smart defaults, all values specified and things only specified once with syntax checking...

The following uses Strings for the values for brevity/speed...

scala> case class Pizza(ingredients: Traversable[String], base: String = "Normal", topping: String = "Mozzarella") defined class Pizza  scala> val p1 = Pizza(Seq("Ham", "Mushroom"))                                                                      p1: Pizza = Pizza(List(Ham, Mushroom),Normal,Mozzarella)  scala> val p2 = Pizza(Seq("Mushroom"), topping = "Edam")                                p2: Pizza = Pizza(List(Mushroom),Normal,Edam)  scala> val p3 = Pizza(Seq("Ham", "Pineapple"), topping = "Edam", base = "Small")        p3: Pizza = Pizza(List(Ham, Pineapple),Small,Edam) 

You can then also use existing immutable instances as kinda builders too...

scala> val lp2 = p3.copy(base = "Large") lp2: Pizza = Pizza(List(Ham, Pineapple),Large,Edam) 
like image 125
James Strachan Avatar answered Oct 14 '22 12:10

James Strachan