如果方法没有完全相同的签名,如何设计抽象类?

时间:2017-09-22 19:37:00

标签: scala oop inheritance abstract-class

这是一个“现实生活”OO设计问题。我正在使用Scala,并对特定的Scala解决方案感兴趣,但我绝对愿意听到一般的想法。

我正在实施分支定界组合优化程序。算法本身很容易实现。对于每个不同的问题,我们只需要实现一个类,其中包含有关搜索允许的邻居状态的信息,如何计算成本,然后可能是下限等等...

我还希望能够尝试不同的数据结构。例如,存储逻辑公式的一种方法是使用简单的整数列表。这表示一组子句,每个整数都是一个字面值。如果我们做一些像“双文字观察名单”这样的事情,我们可以有更好的表现,并存储一些关于公式的额外信息。

这一切都意味着像这样的事情

assign

希望这不是太疯狂,错误或混乱。这里的整个问题是,公式中的这个assign方法通常只需要分配当前的文字。但是,在双字手表的情况下,你正在做一些懒惰的事情,要求你稍后知道之前分配的文字。

解决此问题的一种方法是,您只需在数据结构中保留此先前分配的文字列表,也可以将其作为私有内容。使其成为一个独立的懒惰数据结构。但是,此前任务的列表实际上是使用Formula类的人可以自然获得的。因此,如果有必要,每次Formula允许使用它的人只提供列表是有意义的。

这里的问题是我们现在不能有一个只声明assign(ll:Int):Formula的抽象assign(literal: Int, previous_assignments: Seq[Int])类。在正常情况下这是可以的,但如果这是一个双字的监视列表公式,它实际上是Formula

从使用它的类的角度来看,它是好的。但是,我们如何编写可以采用assign(Int)的所有这些不同版本的通用代码?由于剧烈的签名变化,它不能简单地成为一种抽象方法。我们可以强迫用户始终提供完整的指定变量,但这也是一种谎言。怎么办?

这个想法是监视列表类只是变成一种常规implicit类,如果我写下某种适配器方法,知道从哪里采取以前的赋值...我想也许用{{ 1}}我们可以做些什么。

1 个答案:

答案 0 :(得分:3)

我会尝试让我的回答有点笼统,因为我并不相信我完全完全跟随你的目标。总之...

一般来说,首先应该考虑接受一个普通的超类作为参数。显然,我不能使用IntSeq[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 = ...
}

这些路径中哪一条最好取决于偏好以及它与其余代码的匹配程度。