如果您正在编写使用大量漂亮的、不可变的数据结构的代码,case类似乎是天赐之物,只需一个关键字就可以免费获得以下所有内容:
但是将不可变的数据结构定义为case类有哪些缺点呢?
它对类或其客户端设置了哪些限制?
在某些情况下,您应该选择非案例类吗?
发布于 2016-06-09 12:24:59
在某些情况下,您应该选择非案例类吗?
Martin在他的课程Scala中的函数编程原则 (讲座4.6 -模式匹配)中给了我们一个很好的起点,当我们必须在类和案例类之间进行选择时,我们可以使用它。Scala的例子的第7章包含了相同的示例。
比如说,我们想为算术表达式编写一个解释器。为了一开始就保持简单,我们将自己限制在数字和+操作上。此类表达式可以表示为类层次结构,以抽象基类Expr作为根,并以两个子类Number和Sum表示。然后,表达式1+ (3 + 7)将表示为 新和(新数(1),新和(新数(3),新数(7)
abstract class Expr {
def eval: Int
}
class Number(n: Int) extends Expr {
def eval: Int = n
}
class Sum(e1: Expr, e2: Expr) extends Expr {
def eval: Int = e1.eval + e2.eval
}此外,添加一个新的Prod类不会导致对现有代码的任何更改:
class Prod(e1: Expr, e2: Expr) extends Expr {
def eval: Int = e1.eval * e2.eval
}相反,添加一个新方法需要修改所有现有的类。
abstract class Expr {
def eval: Int
def print
}
class Number(n: Int) extends Expr {
def eval: Int = n
def print { Console.print(n) }
}
class Sum(e1: Expr, e2: Expr) extends Expr {
def eval: Int = e1.eval + e2.eval
def print {
Console.print("(")
print(e1)
Console.print("+")
print(e2)
Console.print(")")
}
}用案例类解决了同样的问题。
abstract class Expr {
def eval: Int = this match {
case Number(n) => n
case Sum(e1, e2) => e1.eval + e2.eval
}
}
case class Number(n: Int) extends Expr
case class Sum(e1: Expr, e2: Expr) extends Expr添加新方法是一种局部更改。
abstract class Expr {
def eval: Int = this match {
case Number(n) => n
case Sum(e1, e2) => e1.eval + e2.eval
}
def print = this match {
case Number(n) => Console.print(n)
case Sum(e1,e2) => {
Console.print("(")
print(e1)
Console.print("+")
print(e2)
Console.print(")")
}
}
}添加一个新的Prod类可能需要更改所有模式匹配。
abstract class Expr {
def eval: Int = this match {
case Number(n) => n
case Sum(e1, e2) => e1.eval + e2.eval
case Prod(e1,e2) => e1.eval * e2.eval
}
def print = this match {
case Number(n) => Console.print(n)
case Sum(e1,e2) => {
Console.print("(")
print(e1)
Console.print("+")
print(e2)
Console.print(")")
}
case Prod(e1,e2) => ...
}
}视频节目4.6模式匹配的记录
这两种设计都是非常好的,它们之间的选择有时是一个风格的问题,但也有一些标准是重要的。 其中一个条件可能是,是更经常地创建新的表达式子类,还是更频繁地创建新的方法?,因此它是一个考虑系统未来可扩展性和可能的扩展传递的标准。 --如果您所做的主要是创建新的子类,那么实际上面向对象的分解解决方案占据了上风。原因是,只要创建一个新的子类就很容易,而且是一个非常本地的更改,在这个子类中,就像函数解决方案一样,您必须返回,并更改eval方法中的代码,并向其中添加一个新的案例。 另一方面,如果您所做的是创建许多新方法,但是类层次结构本身将保持相对稳定,那么模式匹配实际上是有利的。因为,同样,模式匹配解决方案中的每个新方法都只是一个本地更改,无论您将它放在基类中,还是在类层次结构之外。而一个新的方法,比如面向对象的分解中的显示,需要一个新的增量,每个子类都是这样。所以会有更多的部分,你必须去触摸。 因此,这种扩展性在两个维度中的问题--您可能想要向层次结构中添加新的类,或者您可能想要添加新的方法,或者两者兼而有之--被命名为-- expression 。
记住:我们必须把这当作一个起点,而不是像唯一的标准。

https://stackoverflow.com/questions/4653424
复制相似问题