Я создаю простую схему инъекции зависимостей для инъекции конструктора в Scala. Идея заключается в том, что объекты, которые DI'd помещают свои требуемые сервисы в свой конструктор, как обычные параметры, и реализуют класс типов, который определяет, какие из их аргументов берутся из контейнера и которые передаются пользователем при создании экземпляра.Имитировать частичный вывод параметра типа с implicits?
Таким образом, это должно выглядеть примерно так:
trait Container {
private singletons: Map[Class, AnyRef]
def getSingleton[T: Manifest] =
singletons(implicitly[Manifest[T]].erasure).asInstanceOf[T]
... methods for adding singletons, etc ...
}
class Foo(arg: String, svc: FooService) {
...
}
trait Constructor[T] { ??? }
object FooConstructor extends Constructor[Foo] {
def construct(arg: String)(implicit container: Container) =
new Foo(arg, container.getSingleton[FooService])
}
Сейчас в основном я хотел бы иметь возможность иметь метод, называемый construct
, который я могу назвать, как construct[Foo]("asd")
и получить новый экземпляр Foo
с "asd"
переданный конструктору, и FooService
, полученный из локального контейнера и переданный конструктору. Идея состоит в том, что он должен захватить экземпляр класса Constructor
для Foo
и в виде типа, узнать количество и типы аргументов, которые он должен иметь. Кроме того, и это сложная часть, я не хочу записывать типы аргументов - просто объект, который нужно построить.
Я попробовал несколько вещей:
trait Constructor1[T, A] {
def construct(arg: A): T
}
trait Constructor2[T, A1, A2] {
def construct(arg1: A1, arg2: A2): T
}
def construct[T, A](arg1: A): T = implicitly[Constructor1[T, A]].construct(arg1)
...
Этот подход не работает, хотя, потому что кажется, что для того, чтобы «вызвать» экземпляр класса Constructor
типа, мы должны записать типы аргументы, которые много неприятных шаблонный:
construct[Foo, String]("asd") // yuck!
есть ли способ, чтобы использовать классы типов (или что-нибудь еще), чтобы сортировать частично вывести параметры типа? У нас есть типы параметров конструктора для Foo
, определенные в определении экземпляра Constructor
, поэтому, если мы можем вызвать экземпляр, мы должны просто вызвать construct
и получить правильные типы аргументов. Проблема заключается в получении этого экземпляра без указания аргументов типа конструктора. Я играл с кучей различных идей для этого, и я чувствую, что с силой и сумкой трюков Scala там просто имеет, чтобы быть способ, который я могу написать construct[Foo]("asd")
и иметь список аргументов, который будет безопасным. Есть идеи?
ОБНОВЛЕНИЕ: Отличный ответ Майлза Сабина + небольшая модификация, вот метод, который требует только одного параметра типа и работает для всех длин списка аргументов. Это довольно простой способ безболезненного телеграфировать зависимостей, без стоимости отражения:
trait Constructor1[T, A] { def construct(arg1: A)(implicit c: Container): T }
trait Constructor2[T, A, B] { def construct(arg1: A, arg2: B)(implicit c: Container): T }
implicit object FooConstructor extends Constructor1[Foo, String] {
def construct(arg1: String)(implicit c: Container) =
new Foo(arg1, c.getSingleton[FooService])
}
implicit object BarConstructor extends Constructor2[Bar, String, Int] {
def construct(arg1: String, arg2: Int)(implicit c: Container) =
new Bar(arg1, arg2, c.getSingleton[FooService])
}
class Construct[T] {
def apply[A](arg1: A)(implicit ctor: Constructor1[T, A], container: Container) =
ctor.construct(arg1)
def apply[A, B](arg1: A, arg2: B)(implicit ctor: Constructor2[T, A, B], container: Container) =
ctor.construct(arg1, arg2)
}
def construct[T] = new Construct[T]
construct[Foo]("asd")
construct[Bar]("asd", 123)
Спасибо, что ответили, я надеялся, что вы взглянете на него ... Извините, вопрос был настолько длинным! К сожалению, это еще не совсем решает проблему, потому что я не могу сделать перегрузку для работы разных длин списков. Используя этот подход, я должен был бы сказать 'construct1 [Foo] (« asd »)', 'construct2 [Bar] (« asd », 123)» и т. Д. Я попытался добавить неявный параметр доказательства для дифференциации перегрузок из 'construct' (' construct [T] (неявный ev: Constructor [T, _, _]) и т. д.), но он, похоже, не работает ... – lvilnis
Я решил это! Хитрость заключается в том, чтобы не иметь объект Construct1 [T] ', а просто объект' Construct [T] ', который имеет все методы« apply », по одному для каждой длины списка аргументов. Я уточню свой вопрос. Спасибо за вашу помощь! – lvilnis