Возможно ли для какого-либо признака наложить требование, чтобы его реализующие классы реализовали некоторый внутренний класс, который он затем может расширить? Э.Г.
trait BaseTrait {
// not actually an "abstract class", but a requirement that
// subclasses provide a class named Foo with this constructor signature
abstract class Foo(bar: Bar)
def normalFoo(bar: Bar): Foo = new Foo(bar)
// trait needs to be able to extend the Foo class implemented by the subclass.
// this seems to be the impossible part, as far as I can tell...
def fancyFoo(bar: Bar): Foo with SomeMixin = new Foo(bar) with SomeMixin {
def anExtraMethod() = println("I'm an extra!")
}
}
object ThingA extends BaseTrait {
class Foo(bar: Bar) {
def getThingAStuff() = println("I'm part of ThingA")
}
}
object ThingB extends BaseTrait {
class Foo(bar: Bar) {
def getThingBStuff() = println("I'm part of ThingB")
}
}
// calling `fancyFoo` on the concrete implementations should grant
// access to the specific methods in their respective `Foo` classes,
// as well as the "extra method" that the trait adds
val aFoo: ThingA.Foo with SomeMixin = ThingA.fancyFoo(bar)
aFoo.getThingAStuff()
aFoo.anExtraMethod()
val bFoo: ThingB.Foo with SomeMixin = ThingB.fancyFoo(bar)
bFoo.getThingBStuff()
bFoo.anExtraMethod()
Причина, по которой я хочу это, заключается в том, что у меня есть большое количество ThingX
классов, которые в настоящее время вынуждены реализовывать свой собственный эквивалент fancyFoo
(и другие подобные методы, которые требуют добавления Mixin в свои определенный класс Foo). Я хочу сократить шаблон, переместив fancyFoo
и его друзей в BaseTrait, но я не смог придумать ничего менее многословного, чем то, что уже есть.
редактирование:
Мое обобщение, приведенное выше, могло скрыть общее намерение, так что вот некоторые предпосылки:
Мой реальный пример использования вращается вокруг моделирования схемы базы данных и некоторой логики объединения таблиц. Команда начала отходить от «поднятого» синтаксиса Slick и больше к raw sql, и эта система появилась, чтобы помочь в написании необработанных запросов.
Foo
= TableReference
. Каждый из объектов ThingX
представляет конкретную таблицу, а их соответствующие ссылочные классы содержат методы для ссылки на столбцы этой таблицы.
SomeMixin
= TableJoin
, который должен был добавить логику соединения (то есть, как добраться до одной таблицы из другой таблицы). Объекты ThingX
обычно определяют def direct
для получения прямой ссылки на таблицу (т. Е. Начало предложения FROM
в запросе SQL), def from(someOtherRef)
, который создает INNER JOIN
, и def optFrom(someOtherRef)
, который создает LEFT JOIN
. Эти три метода - это то, что я пытался абстрагировать в BaseTrait
.
Я полагаю, что do нужно иметь возможность предоставить простой TableReference
, а также TableReference with TableJoin
, поскольку у нас есть утилита для объединения всей логики соединения, и мы хотим запретить ссылки без какой-либо логики соединения передаются в него. В базе кода есть несколько простых ссылок.
Я надеюсь определить что-то вроде
trait TableSupport {
type Reference <: TableReference
trait CanMatch[Ref] {
// corresponds to the `ON` part of a `JOIN` clause
def matchCondition(self: Reference, other: Ref): RawSQL
}
def defaultAlias: String
// All of the below would be implemented by the `TableSupport` trait
// in terms of the `Reference` constructor and mixing in TableJoin.
// But currently each table companion has to explicitly implement these.
def reference(alias: String = defaultAlias): Reference = ???
def direct(alias: String = defaultAlias): Reference with TableJoin = ???
def from[Ref: CanMatch](ref: Ref, alias: String = defaultAlias): Reference with TableJoin = ???
def optFrom[Ref: CanMatch](ref: Ref, alias: String = defaultAlias): Reference with TableJoin = ???
}
Я застрял в последних четырех методах, описанных выше, поскольку кажется, что они требуют, казалось бы, несуществующей возможности, о которой я спрашиваю в моем исходном вопросе, или для TableSupport
разработчиков, чтобы явно определить отдельные методы для создания Reference
и Reference with TableJoin
, что в конечном итоге сводит на нет цель сокращения шаблонов из-за дополнительного шаблона внедрения этих методов.