Иерархия универсальных типов scala с другим именем атрибута
Я использую https://pureconfig.github.io/ для загрузки значений конфигурации. Например, для каждой таблицы в базе данных я храню (db: String, table: String)
Однако мне нужно обозначить конкретные таблицы. Поэтому у каждого есть отдельная черта. То есть:
trait Thing
trait ThingWithStuff extends Thing {
def value:String
}
trait FooThing extends Thing{
def fooThing: ThingWithStuff
}
trait BarThing extends Thing{
def barThing: ThingWithStuff
}
Все они имеют разные имена атрибутов с одинаковым типом, который в свою очередь содержит db
а также table
, При обработке их некоторыми методами:
def myMethodFoo(thing:FooThing)= println(thing.fooThing)
def myMethodBar(thing:BarThing)= println(thing.barThing)
это приводит к дублированию кода. Пытаясь исправить это с помощью обобщений, я не могу написать такую функцию:
def myMethod[T<: Thing] = println(thing.thing)
так как имя атрибута будет другим. Есть ли умный способ обойти это? Замечания:
table-first {
db = "a"
table = "b"
}
table-second {
db = "foo"
table = "baz"
}
не может иметь один и тот же идентификатор заранее, так как в противном случае он будет перезаписывать каждое значение, чтобы оно содержало только значение последнего элемента для этого идентификатора. Поэтому я прибегнул к использованию разных имен атрибутов (table-first, table-second
или специально для примера: fooThing, barThing
)
Как я могу исправить эту проблему, чтобы предотвратить дублирование кода?
1 ответ
Вот решение с использованием классов типов для FooThing
а также BarThing
:
trait Thing
trait ThingWithStuff {
def value: String
}
trait FooThing extends Thing {
def fooThing: ThingWithStuff
}
trait BarThing extends Thing {
def barThing: ThingWithStuff
}
// Define implicits:
trait ThingEx[SomeThing <: Thing] {
def extract(thing: SomeThing): ThingWithStuff
}
implicit val fooThingEx = new ThingEx[FooThing]{
def extract(thing: FooThing): ThingWithStuff = thing.fooThing
}
implicit val barThingEx = new ThingEx[BarThing]{
def extract(thing: BarThing): ThingWithStuff = thing.barThing
}
// Define the method:
def myMethod[SomeThing <: Thing](thing: SomeThing)(implicit thingEx: ThingEx[SomeThing]) =
println(thingEx.extract(thing).value)
// Try it out:
val foo = new FooThing {
def fooThing = new ThingWithStuff {
def value = "I am a FooThing!"
}
}
val bar = new BarThing {
def barThing = new ThingWithStuff {
def value = "I am a BarThing!"
}
}
myMethod(foo)
myMethod(bar)
Результат:
I am a FooThing!
I am a BarThing!
По сути, мы "создаем" полиморфизм там, где его нет - два неявных ThingEx
позволяют связывать fooThing
а также barThing
все вместе. Вы должны определить эту привязку только один раз - и тогда вы сможете использовать ее везде.
Если специальные классы полиморфизма и типов являются новыми для вас, вы можете начать здесь, например.
Надеюсь, это поможет!