这是一个“现实生活”OO设计问题。我正在使用Scala,并对特定的Scala解决方案感兴趣,但我绝对愿意听到一般的想法。
我正在实施分支定界组合优化程序。算法本身很容易实现。对于每个不同的问题,我们只需要实现一个类,其中包含有关搜索允许的邻居状态的信息,如何计算成本,然后可能是下限等等...
我还希望能够尝试不同的数据结构。例如,存储逻辑公式的一种方法是使用简单的整数列表。这表示一组子句,每个整数都是一个字面值。如果我们做一些像“双文字观察名单”这样的事情,我们可以有更好的表现,并存储一些关于公式的额外信息。
这一切都意味着像这样的事情
assign
希望这不是太疯狂,错误或混乱。这里的整个问题是,公式中的这个assign
方法通常只需要分配当前的文字。但是,在双字手表的情况下,你正在做一些懒惰的事情,要求你稍后知道之前分配的文字。
解决此问题的一种方法是,您只需在数据结构中保留此先前分配的文字列表,也可以将其作为私有内容。使其成为一个独立的懒惰数据结构。但是,此前任务的列表实际上是使用Formula类的人可以自然获得的。因此,如果有必要,每次Formula
允许使用它的人只提供列表是有意义的。
这里的问题是我们现在不能有一个只声明assign(ll:Int):Formula
的抽象assign(literal: Int, previous_assignments: Seq[Int])
类。在正常情况下这是可以的,但如果这是一个双字的监视列表公式,它实际上是Formula
。
从使用它的类的角度来看,它是好的。但是,我们如何编写可以采用assign(Int)
的所有这些不同版本的通用代码?由于剧烈的签名变化,它不能简单地成为一种抽象方法。我们可以强迫用户始终提供完整的指定变量,但这也是一种谎言。怎么办?
这个想法是监视列表类只是变成一种常规implicit
类,如果我写下某种适配器方法,知道从哪里采取以前的赋值...我想也许用{{ 1}}我们可以做些什么。
答案 0 :(得分:3)
我会尝试让我的回答有点笼统,因为我并不相信我完全完全跟随你的目标。总之...
一般来说,首先应该考虑接受一个普通的超类作为参数。显然,我不能使用Int
和Seq[Int]
。
你可以有两种方法;有一个叫另一个。例如,只需将Int
包含到带有一个元素的Seq[Int]
中,然后将其传递给另一个方法。
您也可以将参数包装在某个自定义类中,例如
class Assignment {
...
}
def int2Assignment(n: Int): Assignment = ...
def seq2Assignment(s: Seq[Int]): Assignment = ...
case class Formula[F<:Formula[F]](clauses:List[List[Int]]) {
def assign(ll: Assignment) :F = ...
}
当然,您可以选择隐式使用这些转换方法,以便调用者只需导入它们,而不是显式调用它们。
最后,您可以使用类型类来执行此操作:
trait Assigner[A] {
...
}
implicit val intAssigner = new Assigner[Int] {
...
}
implicit val seqAssigner = new Assigner[Seq[Int]] {
...
}
case class Formula[F<:Formula[F]](clauses:List[List[Int]]) {
def assign[A : Assigner](ll: A) :F = ...
}
您还可以在类级别创建该类型参数:
case class Formula[A:Assigner,F<:Formula[A,F]](clauses:List[List[Int]]) {
def assign(ll: A) :F = ...
}
这些路径中哪一条最好取决于偏好以及它与其余代码的匹配程度。