【发布时间】:2013-12-23 03:52:13
【问题描述】:
我们正在重构继承的method 以改用类型类——我们希望将所有method 实现集中在一个地方,因为将它们分散在实现类中会使维护变得困难。然而,我们遇到了一些麻烦,因为我们对类型类还很陌生。目前method定义为
trait MethodTrait {
def method: Map[String, Any] = // default implementation
}
abstract class SuperClass extends MethodTrait {
override def method = super.method ++ // SuperClass implementation
}
class Clazz extends SuperClass {
override def method = super.method ++ // Clazz implementation
}
等等,总共有 50 多个具体类,层次结构相当浅(abstract class SuperClass -> abstract class SubSuperClass -> abstract class SubSubSuperClass -> class ConcreteClass 尽可能深),一个具体的类永远不会扩展另一个具体的类。 (在实际实现中,method 返回一个 Play Framework JsObject 而不是 Map[String, Any]。)我们正在尝试将其替换为类型类:
trait MethodTrait[T] {
def method(target: T): Map[String, Any]
}
class MethodType {
type M[T] = MethodTrait[T]
}
implicit object Clazz1Method extends MethodTrait[Clazz1] {
def method(target: Clazz1): Map[String, Any] { ... }
}
implicit object Clazz2Method extends MethodTrait[Clazz2] {
def method(target: Clazz2): Map[String, Any] { ... }
}
// and so on
我遇到了两个问题:
一个。模仿先前实现中的super.method ++ 功能。目前我正在使用
class Clazz1 extends SuperClass
class Clazz2 extends SubSuperClass
private def superClassMethod(s: SuperClass): Map[String, Any] = { ... }
private def subSuperClassMethod(s: SubSuperClass): Map[String, Any] = {
superClassMethod(s) ++ ...
}
implicit object Clazz1Method extends MethodTrait[Clazz1] {
def method(target: Clazz1): Map[String, Any] = {
superClassMethod(target) ++ ...
}
}
implicit object Clazz2Method extends MethodTrait[Clazz2] {
def method(target: Clazz2): Map[String, Any] = {
subSuperClassMethod(target) ++ ...
}
}
但这很丑陋,如果我不小心调用了层次结构太远的方法,我不会收到警告或错误,例如如果Clazz2 调用superClassMethod 而不是subSuperClassMethod。
B。在超类上调用 method,例如
val s: SuperClass = new Clazz1()
s.method
理想情况下,我希望能够告诉编译器 SuperClass 的每个子类在类型类中都有一个对应于 method 的隐式对象,因此 s.method 是类型安全的(或者我会得到如果我忽略了为SuperClass 的子类实现相应的隐式对象,则会出现编译时错误),但我所能想到的只是
implicit object SuperClassMethod extends MethodTrait[SuperClass] {
def method(target: SuperClass): Map[String, Any] = {
target match {
case c: Clazz1 => c.method
case c: Clazz2 => c.method
...
}
}
}
这很丑陋,如果我省略了一个类,它不会给我编译时警告或错误,因为我无法将 SuperClass 定义为密封特征。
我们对类型类的替代方案持开放态度,这将使我们能够将method 代码集中在一个地方。 method 只能从两个地方调用:
A.其他method 实现,例如Clazz1 有一个val clazz2: Option[Clazz2],在这种情况下Clazz1 中的method 实现将类似于
def method = super.method ++ /* Clazz1 method implementation */ ++
clazz2.map(_.method).getOrElse(Map())
B.顶级 Play Framework 控制器(即所有控制器继承的抽象类),我们在其中定义了三个 ActionBuilders,它们调用 method,例如
def MethodAction[T <: MethodTrait](block: Request[AnyContent] => T) = {
val f: Request[AnyContent] => SimpleResult =
(req: Request[AnyContent]) => Ok(block(req).method)
MethodActionBuilder.apply(f)
}
【问题讨论】:
标签: oop scala functional-programming polymorphism typeclass