Scala向类中添加可堆叠属性。

3
我有一个简单的类Feature,目前实现为案例类。
case class Feature(value :String)

一个特征可以通过不同的属性进行修饰,比如说有一个函数可以统计该特征出现的次数,这时我可能需要一个CountedFeature。除了计数以外,我还可能需要一个WeightedFeature、一个IndexedFeature等等。

我的直觉告诉我这适合使用traits,因此我定义了以下traits:

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

这里有两个问题: 1. 我需要为每个特征组合创建一个具体的类(例如实现CountedWeightedFeature、CountedIndexedfeature等),还是有办法避免这样做。如果我将添加更多的装饰,那么维护所有组合的类将变得不可能。 2. 我想设计一个基于计数加权特征的函数。它的签名应该类似于:

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

可能存在索引或不存在,因此此功能应该有一种方法来获取一个类并实例化一个新类,其中包含原始类的所有特征以及一个额外的特征。
在Scala中是否有一种优雅的方法来做到这一点,或者我应该完全重新考虑这个设计?
1个回答

0

整体设计在我看来很好,除了不建议扩展 case class。可以在https://dev59.com/Cmcs5IYBdhLWcg3wmlWk#12705634中找到关于此事的简要原因。

因此,您可能需要将 Feature 重写为以下内容:

trait Feature { def value: String }

现在你可以像这样为模式匹配等定义 case classes:
case class CountedFeature(value: String, count: Long) extends Feature with Counted

避免像这样的 case classes 的组合爆炸并不容易,但是您可以随时使用 Feature with Counted 等类型。请记住,您可以轻松地创建与类型 Feature with Counted 匹配的对象。例如:

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

实现像你想要的computeWeightsByCount有点棘手,因为没有简单的方法来构建一个带有权重的T,除非我们更多地了解类型T。但是可以通过隐式方法完成。基本上,我们需要为每个你想要应用此方法的Feature with Counted定义生成T with Weighted的路径从T开始。例如,我们从这里开始:

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

我们想要像您在问题中所做的那样定义computeWeightsByCount,但同时还采用一个隐式方法,该方法接受一个T和一个权重,并生成一个带有权重的T
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开始,这个Feature是从一个带有计数的Feature中得到的。我们将把它放在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“层次结构”的任何后续扩展都可以进行,而无需对computeWeightsByCount进行任何更改。编写新特性的人也可以提供相应的隐式方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接