Scala把方法放在特质或案例类?

Mik*_*yer 15 scala object-oriented-analysis

有两种方法可以为scala中继承相同特征的两个不同类定义方法.

sealed trait Z { def minus: String }
case class A() extends Z { def minus = "a" }
case class B() extends Z { def minus = "b" }
Run Code Online (Sandbox Code Playgroud)

替代方案如下:

sealed trait Z { def minus: String = this match {
    case A() => "a"
    case B() => "b"
}
case class A() extends Z
case class B() extends Z
Run Code Online (Sandbox Code Playgroud)

第一种方法重复方法名称,而第二种方法重复类名称.我认为第一种方法最好使用,因为代码是分开的.但是,我发现自己经常使用第二个方法来处理复杂的方法,因此可以非常容易地添加其他参数,例如:

sealed trait Z {
  def minus(word: Boolean = false): String = this match {
    case A() => if(word) "ant" else "a"
    case B() => if(word) "boat" else "b"
}
case class A() extends Z
case class B() extends Z
Run Code Online (Sandbox Code Playgroud)

这些做法之间有什么其他差异?如果我选择第二种方法,是否还有等待我的错误?

编辑: 我引用了开放/封闭原则,但有时,我不仅要修改函数的输出,这取决于新的案例类,还要修改输入,因为代码折射.有没有比第一个更好的模式?如果我想在第一个示例中添加先前提到的功能,这将产生重复输入的丑陋代码:

sealed trait Z { def minus(word: Boolean): String  ; def minus = minus(false) }
case class A() extends Z { def minus(word: Boolean) = if(word) "ant" else "a" }
case class B() extends Z { def minus(word: Boolean) = if(word) "boat" else "b" }
Run Code Online (Sandbox Code Playgroud)

Mik*_*378 7

我会选择第一个.

为什么?仅保持开放/封闭原则.

实际上,如果你想添加另一个子类,让我们说case class C,你必须修改supertrait/superclass来插入新的条件...丑陋

您的场景在Java中具有类似于模板/策略模式的条件.

更新:

在您的上一个场景中,您无法避免输入的"重复".实际上,Scala中的参数类型是不可推断的.

最好是使用内聚方法而不是将整个内部混合在一个方法中,该方法提供与union期望的方法一样多的参数.

想象一下你的supertrait方法中的十个条件.如果你无意中改变了每个人的行为怎么办?每次更改都会有风险,每次修改时都应该运行supertrait单元测试...

此外,无意中改变输入参数(不是行为)根本不是"危险的".为什么?因为编译器会告诉你参数/参数类型不再相关.如果你想改变它并为每个子类做同样的事情...问你的IDE,它喜欢一键重构这样的事情.

正如此链接所解释的:

为什么开放式原则很重要:

无需单元测试.
无需了解重要且庞大的类的源代码.
由于绘图代码被移动到具体的子类,因此在添加新功能时会降低影响旧功能的风险.

更新2:

这里有一个避免输入重复的样本符合您的期望:

sealed trait Z { 
     def minus(word: Boolean): String = if(word) whenWord else whenNotWord
     def whenWord: String
     def whenNotWord: String             
  }

case class A() extends Z { def whenWord = "ant"; def whenNotWord = "a"}
Run Code Online (Sandbox Code Playgroud)

谢谢类型推断:)


Xav*_*hot 1

从 开始Scala 3,您可以使用特征参数(就像类有参数一样),这在这种情况下简化了很多事情:

trait Z(x: String) { def minus: String = x }
case class A() extends Z("a")
case class B() extends Z("b")
A().minus // "a"
B().minus // "b"
Run Code Online (Sandbox Code Playgroud)