63

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?

keiter
  • 3,534
  • 28
  • 38
Jakub Korab
  • 4,974
  • 2
  • 24
  • 34

5 Answers5

59

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)
James Strachan
  • 9,168
  • 34
  • 31
  • I think that's really neat. I think I need to stick my head into case classes. Thanks! – Jakub Korab Jan 07 '11 at 13:34
  • 10
    Is this a good solution when the intent is to use the builder from Java? I think maybe not. – Landon Kuhn Aug 03 '15 at 21:08
  • 6
    Sorry but this answer is absurd. – Rob Nov 01 '17 at 22:08
  • Unfortunately this doesn't handle the common use case where you might want to optionally specify some parameters and leave others at their default values. (Or at least, you can't do so without repeating the default value passed in as a parameter.) – Michael Mior Apr 26 '23 at 17:34
33

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...

wilx
  • 17,697
  • 6
  • 59
  • 114
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 2
    I don't understand why the pizzabuilder itself needs to be immutable. Seems extravagent, imperfomant, and unnessesary. In the end it's just a transitory object that builds an immutable pizza – Andrew Norman Jun 02 '16 at 18:06
  • 4
    @AndrewNorman I haven't suggested using an immutable builder, the question asked for one. The first suggestion I made was to keep using the Java pattern, in fact. On the other hand, the mutability prevents reuse, so there might be situations where it does help. Also, short-lived heap objects is something the Java GC is optimized for, and they'll should spend their entire life on L3 if not L2 cache, so performance is not that big of an issue. – Daniel C. Sobral Jun 02 '16 at 19:51
  • the thing is that this should be a "build-er" pattern and not a "built" pattern. The problem with making the builder itself immutable is that: 1.for a single threaded system, having it construct a copy of a new builder with every call is unnecessary overhead and 2. if this builder was used in a multithreaded scenario, then since every call starts spawning new versions of the builder in each thread with each containing incomplete sets of the total content! – Andrew Norman Jun 02 '16 at 20:50
  • 2
    What you (@AndrewNorman) are missing is that with an immutable builder, you can save intermediate states. You can then build off of that intermediate state over and over again. That is something you can't do if you have a builder that is changing itself as you go. – John Arrowwood Feb 01 '17 at 00:31
  • @JohnArrowwood in that case why not use a cache for the reusable "prefab" bits? That way you avoid being forced to take a performance / memory hit any time you add the non static intermediate state. – Andrew Norman Feb 02 '17 at 18:55
  • 1
    Only way to know for sure is to benchmark it. Which solution is easier to code? Which is easier to understand? Which one carries the most surprising gotcha's to the unwary developer who has never looked at the implementation? And which one will actually incur a measurable penalty. My guess is having to use a "cache" is far more complicated (from a code usage perspective) than just assigning the builder to a variable and using it to build multiple objects. And just how big is that performance penalty, anyway, from an empirical perspective? – John Arrowwood Feb 03 '17 at 01:46
  • That post you link to seemed to start out well, but was a dud. Sheesh this should not be such a hard problem. Making Java look good here (with Bloch's static builder pattern). – Rob Nov 01 '17 at 22:11
  • The blog post link is dead. – wilx Apr 23 '19 at 06:04
13

Case classes solve the problem as shown in previous answers, but the resulting api is difficult to use from java when You have scala collections in your objects. To provide a fluent api to java users try this:

case class SEEConfiguration(parameters : Set[Parameter],
                               plugins : Set[PlugIn])

case class Parameter(name: String, value:String)
case class PlugIn(id: String)

trait SEEConfigurationGrammar {

  def withParameter(name: String, value:String) : SEEConfigurationGrammar

  def withParameter(toAdd : Parameter) : SEEConfigurationGrammar

  def withPlugin(toAdd : PlugIn) : SEEConfigurationGrammar

  def build : SEEConfiguration

}

object SEEConfigurationBuilder {
  def empty : SEEConfigurationGrammar = SEEConfigurationBuilder(Set.empty,Set.empty)
}


case class SEEConfigurationBuilder(
                               parameters : Set[Parameter],
                               plugins : Set[PlugIn]
                               ) extends SEEConfigurationGrammar {
  val config : SEEConfiguration = SEEConfiguration(parameters,plugins)

  def withParameter(name: String, value:String) = withParameter(Parameter(name,value))

  def withParameter(toAdd : Parameter) = new SEEConfigurationBuilder(parameters + toAdd, plugins)

  def withPlugin(toAdd : PlugIn) = new SEEConfigurationBuilder(parameters , plugins + toAdd)

  def build = config

}

Then in java code the api is really easy to use

SEEConfigurationGrammar builder = SEEConfigurationBuilder.empty();
SEEConfiguration configuration = builder
    .withParameter(new Parameter("name","value"))
    .withParameter("directGivenName","Value")
    .withPlugin(new PlugIn("pluginid"))
    .build();
EIIPII
  • 1,791
  • 1
  • 17
  • 10
11

It's the same exact pattern. Scala allows for mutation and side effects. That said, if you'd like to be more of a purest, have each method return a new instance of the object that you're constructing with the element(s) changed. You could even put the functions within the Object of a class so that there's a higher level of separation within your code.

class Pizza(size:SizeType, layers:List[Layers], toppings:List[Toppings]){
    def Pizza(size:SizeType) = this(size, List[Layers](), List[Toppings]())

object Pizza{
    def onTopOf( layer:Layer ) = new Pizza(size, layers :+ layer, toppings)
    def withTopping( topping:Topping ) = new Pizza(size, layers, toppings :+ topping)
}

so that your code might look like

val myPizza = new Pizza(Large) onTopOf(MarinaraSauce) onTopOf(Cheese) withTopping(Ham) withTopping(Pineapple)

(Note: I've probably screwed up some syntax here.)

wheaties
  • 35,646
  • 15
  • 94
  • 131
  • If I'm reading that correctly (and my interpretation of Scala behaviour is right), would this not mean that you can call `withTopping(Ham)` on just the `Pizza` object? Wouldn't that cause a crash of some kind (sorry don't have the REPL on this PC)? – Jakub Korab Jan 07 '11 at 13:26
  • 4
    I am pretty sure that the 4th line `object Pizza{` should not be there. Without it, you get all the builder methods for the *class* `Pizza`. Additionally, multiple constructors are defined via `def this(size: SizeType)` (unlike Java's syntax). But I would use default parameters anyway: `class Pizza(size: SizeType, layers: List[Layers] = List(), toppings: List[Toppings] = List())`. – r0estir0bbe Oct 28 '15 at 14:28
0

using Scala partial applies are feasible if you are building a smallish object that you don't need to pass over method signatures. If any of those assumptions don't apply, I recommend using a mutable builder to build an immutable object. With this being scala you could implement the builder pattern with a case class for the object to build with a companion as the builder.

Given that the end result is a constructed immutable object I don't see that it defeats any of the Scala principles.

Andrew Norman
  • 843
  • 9
  • 22