适用于2D和3D矢量的类层次结构

时间:2011-01-23 11:46:26

标签: oop scala architecture hierarchy

我希望有一个通用的矢量抽象类/特征来指定某些方法,例如:

trait Vec 
{
  def +(v:Vec):Vec
  def *(d:Double):Vec

  def dot(v:Vec):Double
  def norm:Double
}

我希望Vec2DVec3D延长Vec

class Vec2D extends Vec { /* implementation */ }
class Vec3D extends Vec { /* implementation */ }

但是,我怎样才能使Vec2D只能添加到其他Vec2D而不是Vec3D

现在我只是在没有共同Vec2D祖先的情况下实施Vec3DVec,但是重复代码会变得乏味。我必须实现依赖于这些类的所有几何类(例如TrianglePolygonMesh,...)两次,一次用于Vec2D,另一次用于{ {1}}。

我看到java实现:Vec3Djavax.vecmath.Vector2d没有共同的祖先。这是什么原因?有没有办法在scala中克服它?

4 个答案:

答案 0 :(得分:7)

您可以使用自我类型:

trait Vec[T] { self:T =>
  def +(v:T):T
  def *(d:Double):T

  def dot(v:T):Double
  def norm:Double
}

class Vec2D extends Vec[Vec2D] { /* implementation */ }
class Vec3D extends Vec[Vec3D] { /* implementation */ }

但如果两个实现非常相似,您也可以尝试在Dimension上进行抽象。

sealed trait Dimension
case object Dim2D extends Dimension
case object Dim3D extends Dimension

sealed abstract class Vec[D <: Dimension](val data: Array[Double]) { 

  def +(v:Vec[D]):Vec[D] = ...
  def *(d:Double):Vec[D] = ...

  def dot(v:Vec[D]):Double = ...
  def norm:Double = math.sqrt(data.map(x => x*x).sum)
}

class Vec2D(x:Double, y:Double) extends Vec[Dim2D.type](Array(x,y))
class Vec3D(x:Double, y:Double, z:Double) extends Vec[Dim3D.type](Array(x,y,z))

当然,这取决于您希望如何表示数据,以及您是否希望拥有可变或不可变的实例。对于“真实世界”的应用程序,您应该考虑http://code.google.com/p/simplex3d/

答案 1 :(得分:5)

作为requested,设计基本特征的最有用方法包括CRTP self-type annotation

trait Vec[T <: Vec[T]] { this: T =>
  def -(v: T): T
  def *(d: Double): T

  def dot(v: T): Double
  def norm: Double = math.sqrt(this dot this)
  def dist(v: T) = (this - v).norm
}

如果没有自我类型,则无法调用this.dot(this),因为dot期望T;因此我们需要使用注释强制执行它。

另一方面,如果没有CRTP,我们将无法在norm上致电(this - v),因为-会返回T,因此我们需要确保类型T具有此方法,例如声明T Vec[T]

答案 2 :(得分:4)

我不确定正确的Scala语法,但您可以实现CRTP,即通过通用参数定义实际类型。

trait Vec[V <: Vec[V]] {
  def +(v:V):V
  ...
}

class Vec2D extends Vec[Vec2D] { }
class Vec3D extends Vec[Vec3D] { }

class Polygon[V <: Vec[V]] {
  ...
}

答案 3 :(得分:2)

在JVM上使用具有CRTP模式的共同祖先存在一个大问题。当您使用不同的实现执行相同的抽象代码时,JVM将对代码进行去优化(无内联+虚拟调用)。如果您只使用Vec3D进行测试,您将不会注意到这一点,但如果您使用Vec2D和Vec3D进行测试,您将看到性能大幅下降。此外,Escape Analysis不能应用于去优化代码(没有标量替换,没有新实例的消除)。 缺少这些优化会使您的程序减慢3倍(非常全面的猜测取决于您的代码)。

尝试一些运行约10秒的基准测试。在与Vec2D相同的运行测试中,然后是Vec3D,然后是Vec2D,然后是Vec3D。你会看到这种模式:

  • Vec2D~10秒
  • Vec3D~30秒
  • Vec2D~30秒
  • Vec3D~30秒