Scala将可堆叠属性添加到类中

时间:2014-05-03 08:55:04

标签: scala design-patterns

我有一个简单的类功能,目前作为案例类实现

case class Feature(value :String)

有多个操作装饰具有不同属性的特征,例如,有一个函数可以计算特征的出现次数,因此我可能需要一个CountedFeature。除了计数,我可能还需要WeightedFeature,IndexedFeature等等。

我的直觉说它适合于特征所以我定义了以下特征

trait Counted {def count :Long}
trait Weighted {def weight :Double}
trait Indexed {def index :Int}

弹出两个问题: 1.我是否需要创建一个实现每个特征组合的具体类(e.x.实现CountedWeightedFeature,CountedIndexedfeature等),或者有一些方法可以避免它。如果我要转向更多的装饰品,就不可能为所有组合保留课程。 2.我想设计一个根据计数对功能进行加权的功能。它的签名应该类似于:

def computeWeightsByCount(features :List[T <: Feature with Counted]) :List[T with Weighted] = {...}

这里可能是索引与否,所以这个函数应该有一些方法来获取一个类并实现一个新类,它具有堆叠在内部的原始类的所有特征以及另外一个。

在Scala中有一些优雅的方法可以做到这一点,还是我应该重新考虑这个设计?

1 个答案:

答案 0 :(得分:0)

除了扩展案例类之外,设计对我来说很好。可以在此处找到原因的简短摘要:https://stackoverflow.com/a/12705634/2186890

所以你可能想要像这样重写Feature

trait Feature { def value: String }

现在您可以为模式匹配等定义案例类,如下所示:

case class CountedFeature(value: String, count: Long) extends Feature with Counted

没有简单的方法可以避免像这样的case类的组合爆炸,但是你可以在任何你喜欢的地方使用Feature with Counted等类型。请记住,您可以轻松创建与Feature with Counted类型匹配的对象。例如:

val x: Feature with Counted = new Feature with Counted { val value = ""; val count = 0L }

实现computeWeightsByCount就像你想要的那样有点棘手,因为在不了解类型T with Weighted的情况下,没有简单的方法来构建T。但它可以用隐式方法完成。从本质上讲,我们需要有一个定义的路径,用于为要应用此方法的每个T with WeightedT生成Feature with Counted。例如,我们从这开始:

trait Feature { def value: String }
trait Counted { def count: Long }
trait Weighted { def weight: Double }
trait Indexed { def index: Int }

我们希望像您在问题中所做的那样定义computeWeightsByCount,但也采用隐式方法,该方法需要T和权重,并生成T with Weighted

def computeWeightsByCount[
  T <: Feature with Counted](                                                                                       
  features: List[T])(
  implicit weighted: (T, Double) => T with Weighted
): List[T with Weighted] = {  
  def weight(fc: Feature with Counted): Double = 0.0d
  features map { f => weighted(f, weight(f)) }
}

现在我们需要定义一个隐式方法来从输入要素中生成加权要素。让我们从Feature with Counted with Weighted获取Feature with Counted。我们将其放在Feature的伴随对象中:

object Feature {
  implicit def weight(fc: Feature with Counted, weight: Double): Feature with Counted with Weighted = {
    case class FCW(value: String, count: Long, weight: Double) extends Feature with Counted with Weighted
    FCW(fc.value, fc.count, weight)
  }
}

我们可以像这样使用它:

case class FC(value: String, count: Long) extends Feature with Counted
val fcs: List[Feature with Counted] = List(FC("0", 0L), FC("1", 1L))
val fcws: List[Feature with Counted with Weighted] = computeWeightsByCount[Feature with Counted](fcs)

对于要计算加权计数的任何类型,您需要定义类似的隐式方法。

不可否认,这远非一个美丽的解决方案。所以,是的,你是对的,你可能想重新考虑设计。然而,这种方法的优点是对Feature&#34;层次结构的任何进一步扩展&#34;可以在不需要对computeWeightsByCount进行任何更改的情况下制作。编写新特征的人也可以提供适当的隐式方法。