我有一个简单的类Feature,目前作为case类实现
case class Feature(value :String)
有多个操作用不同的属性装饰一个特征,例如,有一个函数可能会计算特征的出现次数,所以我可能需要CountedFeature。除了计数,我可能还需要一个WeightedFeature,一个IndexedFeature等。
我的直觉告诉我它适合性状,所以我定义了以下性状
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] = {...}
这里的T可能是索引的,也可能不是索引的,所以这个函数应该有一些方法来获取一个类并实例化一个新类,这个新类具有原始类的所有特征,并在里面堆叠一个额外的特征。
在Scala中是否有一些优雅的方法来做到这一点,或者我是否应该完全重新考虑这个设计?
这个设计看起来不错,除了不推荐扩展case类。在这里可以找到原因的简要总结:https://stackoverflow.com/a/12705634/2186890
所以你可能想把Feature
重写成这样:
trait Feature { def value: String }
现在你可以为模式匹配等定义case类,像这样:
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
类型的情况下,没有简单的方法来构建T with Weighted
。但它可以用隐式方法来完成。从本质上讲,我们需要有一个定义的路径,以便为您想要应用此方法的每个Feature with Counted
从T
生成T with Weighted
。例如,我们这样开始:
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
中获得Feature with Counted with Weighted
开始。我们将把它放在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
进行任何更改的情况下进行。不管是谁写的新trait,都可以提供合适的隐式方法