密封具有许多子类的类

时间:2015-01-10 21:36:39

标签: scala

我将我的一个C ++程序移植到Scala中。在该项目中,有组织的层次结构中有数百个用户定义的类。如果我密封其中一个顶级抽象类,根据Scala规则,我必须将该类的所有子类型的定义放在与密封类/特征相同的文件中。对于我的一个类层次结构,它意味着在该文件中定义大约30个类。

在我的C ++程序中,这30个类位于它们自己的头文件和实现文件中,使它们更易于维护和读取。我担心如果我将这30个类/对象的定义放在我的Scala应用程序中的一个文件中,它将使它们难以维护和阅读。

密封课程的原因是我可以在这些类型的模式匹配时进行详尽的搜索。任何有关组织Scala类层次结构的正确方向的帮助都将不胜感激。

2 个答案:

答案 0 :(得分:3)

在单独的类中执行此操作会有点痛苦,但它可能比将所有内容放在一个巨大的文件中要痛苦一些。首先,您需要确保将所有文件一起编译。然后,在您确保所有内容都已密封的文件中,执行以下操作:

trait GenericA { def foo: Int }
sealed trait A extends GenericA
case class B() extends A with ImplB {}
case class C() extends A with ImplC {}
...

诀窍在于超级类中的所有内容(如果您愿意,它可以是抽象类而不是特征)进入GenericA。但您实际上从未在代码中使用 GenericA,只需使用A即可。现在,您可以为每个实现编写一堆单独的文件,定义如下:

// File #1
trait ImplB extends GenericA { def foo = 7 }

// File #2
trait ImplC extends GenericA { def foo = 4 }

...

现在您将实现分离出来(至少那些可以用GenericA表示的部分)。

如果您还需要案例类参数,该怎么办?没问题 - 只是把它们作为特质的一部分。

// Main file
case class D(i: Int) extends A with ImplD {}

// Separate file
trait ImplD {
  def i: Int
  def foo = i*3
}

这是一项额外的工作,因为您必须在两个位置重复案例类参数,但在您的情况下,它可能是值得的。

答案 1 :(得分:2)

假设您的类是具有许多方法的case-classes(可以使您的文件增长),您可以尝试使用类型类将定义与实现分开(但有时它可能会影响编译器的性能),例如:

Model.scala

sealed trait A
case class A1(a: Int) extends A
case class A2(a: Int) extends A
case class A3(a: Int, b: Int) extends A
...
case class A1(a: Int) extends A

ImplA1.scala

package org.impl
implicit class ImplA1(a: A1) {
   def method1() = a.a + a.a
}

ImplA2.scala

package org.impl
implicit class ImplA2(a: A2) {
   def method1() = a.a * 2
}

用法:

import org.impl._
val a1 = new A1
a1.method1()