我有
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点:
我如何注入(inject)特征“Builder”的所有实现? 我发现了一堆朝着相同方向发展的问题(使用 multibinder、TypeLiteral 等,但没有一个面临注入(inject)所有实现的问题。它只是关于“如何注入(inject)特定实现”)我知道如何绑定(bind)多个实例使用多重绑定(bind)器;但如果它是一个泛型类则不然......
最后我想使用某种外观模式。有一个“构建器”要注入(inject),它会注入(inject)所有实现并知道需要什么构建器(请参见上面的 match-case-frament)。但我没有使用匹配大小写,而是关注了 MapBinder。类似于将 Builder 实现绑定(bind)到 Map,使用类作为键。
例如(伪代码)
class BuildingComponent @Inject()(builder: Map[Class,Builder]){
def doIt(item: Any) = {
builder.get(item.class).build(item)
}
}
最佳答案
Guice 仅初始化它知道的类。因此,您可以将所有实现注入(inject)到外观中,并根据需要对它们进行排序。每次添加新的实现时,您都需要更改此外观..所以不太好..
替代方案
要动态地向 guice 通报您的实现,您需要一些反射(reflection)。如果您可以将 Builder
作为 sealed
特征(获取您可以找到的所有子类的示例 here )或使用第三方库(例如 reflections ),则可以使用标准 scala。
我将解释最后一个案例
您需要在 build.sbt 中导入:
libraryDependencies ++= Seq(
"com.google.inject.extensions" % "guice-multibindings" % "<your guice version>",
"org.reflections" % "reflections" % "0.9.11")
您需要创建模块并通知 guice,例如如果是播放框架,您需要放入 application.conf
play.modules.enabled += "com.example.MyModule"
现在,我假设您能够将所有实现放入同一个包中(您可以检查文档如何在其他情况下获取所有实现)。假设它是com.example.builders
。另外,在我的示例中,我假设您能够将类型参数 T
和 K
移动到类型别名中(使用泛型,绑定(bind)和注入(inject)将有更多技巧 - 您可以尝试自己找到这个方法)。你的构建器将是:
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)
}
}
}
最后,您可以在需要的地方注入(inject)builders: java.util.Set[Builder]
。
更新(添加了如何处理类型化实现的示例)
例如:您将需要额外的类来保存您的砖 block 类型信息。一旦 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)
关于scala - 使用 guice 注入(inject)游戏中某个特征/类的所有实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49409608/