我将我的一个C ++程序移植到Scala中。在该项目中,有组织的层次结构中有数百个用户定义的类。如果我密封其中一个顶级抽象类,根据Scala规则,我必须将该类的所有子类型的定义放在与密封类/特征相同的文件中。对于我的一个类层次结构,它意味着在该文件中定义大约30个类。
在我的C ++程序中,这30个类位于它们自己的头文件和实现文件中,使它们更易于维护和读取。我担心如果我将这30个类/对象的定义放在我的Scala应用程序中的一个文件中,它将使它们难以维护和阅读。
密封课程的原因是我可以在这些类型的模式匹配时进行详尽的搜索。任何有关组织Scala类层次结构的正确方向的帮助都将不胜感激。
答案 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()