What is the Scala equivalent to a Java builder pattern?

前端 未结 5 573
遇见更好的自我
遇见更好的自我 2020-12-23 13:31

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(Ingredien

5条回答
  •  攒了一身酷
    2020-12-23 13:56

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

提交回复
热议问题