Внедрить все реализации определенной черты / класса в игру, используя guice
Я имею
trait Builder[T, K] {
def build(brick: T) : K
Для этой черты у меня есть несколько реализаций...
class StringBuilder extends Builder[Foo, String] { ... }
class HouseBuilder extends Builder[Baa, House] { ... }
class BaaBuilder extends Builder[Baz, Int] { ... }
В зависимости от данного типа, я хотел бы выбрать одну реализацию. Примерно так (псевдокод):
class BuildingComponent @Inject()(builder: Set[Builder]){
def doIt(item: Any) = {
item match {
case _: Foo => builder.filter(Foo).build(item)
case _: Baa => builder.filter(Baa).build(item)
case _: Baz => builder.filter(Baz).build(item)
}
}
Итак, 2 балла:
Как я мог внедрить все реализации черты "Строитель"?? Я нашел кучу вопросов, которые идут в одном направлении (с использованием multibinder, TypeLiteral и т. Д., Но ни один из них не сталкивался с проблемой внедрения всех реализаций. Это просто о том, "как внедрить конкретную реализацию"). Я знаю, как связать несколько экземпляров. использование мультибиндера; но не, если это универсальный класс...
В конце я хотел бы использовать вид фасада. наличие одного "строителя" для внедрения, который вводит все реализации и знает, какой сборщик необходим (см. раздел соответствия случаев выше). Но вместо использования match-case я следил за MapBinder. Что-то вроде привязки реализаций Builder к карте, которая использует класс в качестве ключа.
например (псевдокод)
class BuildingComponent @Inject()(builder: Map[Class,Builder]){
def doIt(item: Any) = {
builder.get(item.class).build(item)
}
}
1 ответ
Guice инициализирует только те классы, о которых он знает. Таким образом, вы можете внедрить все реализации в ваш фасад и заказать их, как вы хотите. Вам нужно будет менять этот фасад каждый раз, когда вы добавляете новую реализацию. Так что не очень хорошо..
альтернатива
Чтобы динамически проинформировать Guice о ваших реализациях, вам нужно подумать. Вы можете использовать стандартную Scala, если вы можете иметь Builder
как sealed
trait (пример получения всех подклассов, которые вы можете найти здесь) или с библиотекой третьей стороны (например, отражения).
Я объясню последний случай
Вам понадобится импорт в вашем build.sbt:
libraryDependencies ++= Seq(
"com.google.inject.extensions" % "guice-multibindings" % "<your guice version>",
"org.reflections" % "reflections" % "0.9.11")
Вам нужно будет создать модуль и уведомить об этом руководство, например, в случае игрового фреймворка вам нужно будет поместить в application.conf
play.modules.enabled += "com.example.MyModule"
Теперь я предполагаю, что вы можете поместить все свои реализации в один и тот же пакет (вы можете проверить документы, как получить все реализации в других случаях). Скажем так com.example.builders
, Также в моем примере я предполагаю, что вы можете перемещать параметры типа T
а также K
в псевдонимы типов (с дженериками будет немного больше трюков с привязкой и внедрением - вы можете попытаться найти этот путь самостоятельно). И ваш строитель будет:
trait Builder {
type T
type K
def build(brick: T) : K
}
Теперь к вашему модулю MyModule
:
package com.example
import com.google.inject.AbstractModule
import com.google.inject.multibindings.Multibinder
import org.reflections.Reflections
class MyModule extends AbstractModule {
override def configure(): Unit = {
import scala.collection.JavaConverters._
val r = new Reflections("com.example.builders")
val subtypes = r.getSubTypesOf(classOf[Builder])
val executorBinder = Multibinder.newSetBinder(binder(), classOf[Builder])
subtypes.asScala.foreach {clazz =>
executorBinder.addBinding().to(clazz)
}
}
}
Наконец, вы можете ввести builders: java.util.Set[Builder]
где вам это нужно.
Обновление (добавлен пример обработки типизированной реализации)
Например: вам понадобится дополнительный класс для хранения информации о типе вашего кирпича. я использовал abstract class
как только Builder
это черта должно быть хорошо
import scala.reflect.runtime.universe._
trait Builder[T, K] {
def build(brick: T): K
}
abstract class TypeChecker[T: TypeTag] {
this: Builder[T, _] =>
def isDefinedAt[C: TypeTag](t: C) = {
typeOf[C] =:= typeOf[T]
}
}
class Foo
class Baa
class House
// first builder implementation
class StringBuilder
extends TypeChecker[Foo]
with Builder[Foo, String] {
override def build(brick: Foo) = {
println("StringBuilder")
""
}
}
// second builder implementation
class HouseBuilder
extends TypeChecker[Baa]
with Builder[Baa, House] {
override def build(brick: Baa) = {
println("HouseBuilder")
new House
}
}
// our set of builders
val s: Set[Builder[_, _] with TypeChecker[_]] = Set(
new StringBuilder,
new HouseBuilder
)
// here we check and apply arrived brick on our set of builders
def check[T: TypeTag](t: T) =
s.filter(_.isDefinedAt(t)).
foreach {b => b.asInstanceOf[Builder[T, _]].build(t)}
check(new Foo)
check(new Baa)