什么是 Scala 等同于 Java 构建器模式?

2022-08-31 15:52:25

在我每天使用Java所做的工作中,我经常使用构建器来获得流畅的界面,例如:new PizzaBuilder(Size.Large).onTopOf(Base.Cheesy).with(Ingredient.Ham).build();

使用快速且肮脏的Java方法,每个方法调用都会改变构建器实例并返回 。不可变地,它涉及更多的键入,在修改之前先克隆构建器。构建方法最终会对构建器状态执行繁重的工作。this

在Scala中实现相同目标的好方法是什么?

如果我想确保它只被调用一次,然后随后只被调用并且可以调用,a-la定向构建器,我将如何解决这个问题?onTopOf(base:Base)with(ingredient:Ingredient)build():Pizza


答案 1

Scala 2.8 中 Builder 模式的另一种替代方法是使用具有默认参数和命名参数的不可变大小写类。它有点不同,但效果是智能默认值,指定所有值,并且只有语法检查指定一次的东西......

以下内容使用字符串作为简洁/速度的值...

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)

然后,您也可以使用现有的不可变实例作为构建器...

scala> val lp2 = p3.copy(base = "Large")
lp2: Pizza = Pizza(List(Ham, Pineapple),Large,Edam)

答案 2

您有三个主要选择。

  1. 使用与 Java、类和所有模式相同的模式。

  2. 使用命名参数和默认参数以及复制方法。Case 类已经为您提供了此功能,但这里有一个示例不是 case 类,只是为了让您更好地理解它。

    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. 使用类型安全生成器模式。我所知道的最好的介绍是这个博客,其中还包含有关该主题的许多其他文章的引用。

    基本上,类型安全生成器模式保证在编译时提供所有必需的组件。人们甚至可以保证相互排除选项或灵活性。成本是构建器代码的复杂性,但是...


推荐