Мы реорганизуем унаследованный 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
.
Б. Вызов 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)
}