我们正在重构继承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框架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
.
乙.召唤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]
,在这种情况下,method
实现Clazz1
将是类似的
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) }
0__.. 6
我认为类型类与您的场景不兼容.当类型不相交时,它们很有用,但实际上要求实例反映超类型/子类型层次结构并且不是独立的.
通过这种重构,您只是创建了挑选错误实例的危险:
trait Foo case class Bar() extends Foo trait HasBaz[A] { def baz: Set[Any] } implicit object FooHasBaz extends HasBaz[Foo] { def baz = Set("foo") } implicit object BarHasBaz extends HasBaz[Bar] { def baz = FooHasBaz.baz + "bar" } def test[A <: Foo](x: A)(implicit hb: HasBaz[A]): Set[Any] = hb.baz val bar: Foo = Bar() test(bar) // boom!
所以你最终用你的模式匹配器重写了多态分派SuperClassMethod
.你基本上去OO - > FP - > OO,同时渲染类型类的想法不可用(要打开),最后是和类型(已知的所有子类型).
我认为类型类与您的场景不兼容.当类型不相交时,它们很有用,但实际上要求实例反映超类型/子类型层次结构并且不是独立的.
通过这种重构,您只是创建了挑选错误实例的危险:
trait Foo case class Bar() extends Foo trait HasBaz[A] { def baz: Set[Any] } implicit object FooHasBaz extends HasBaz[Foo] { def baz = Set("foo") } implicit object BarHasBaz extends HasBaz[Bar] { def baz = FooHasBaz.baz + "bar" } def test[A <: Foo](x: A)(implicit hb: HasBaz[A]): Set[Any] = hb.baz val bar: Foo = Bar() test(bar) // boom!
所以你最终用你的模式匹配器重写了多态分派SuperClassMethod
.你基本上去OO - > FP - > OO,同时渲染类型类的想法不可用(要打开),最后是和类型(已知的所有子类型).