使用guice注入游戏中某个特征/类的所有实现 [英] Inject all implementations of a certain trait/class in play using guice

查看:141
本文介绍了使用guice注入游戏中某个特征/类的所有实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有

trait Builder[T, K] {
    def build(brick: T) : K

为此,我有多种实现方式...

For that trait, i have multiple implementations...

class StringBuilder extends Builder[Foo, String] { ... }
class HouseBuilder  extends Builder[Baa, House]  { ... }
class BaaBuilder    extends Builder[Baz, Int]    { ... }

取决于给定的类型,i想从一种实现中选择。诸如此类(伪代码):

Depending on a given Type, i would like to choose from one implementation. Something like this (Pseudo-code):

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点:


  1. 我如何注入特征 Builder的所有实现?
    我发现了一系列朝着同一方向发展的问题(使用multibinder,TypeLiteral等。但是它们都没有面临注入所有实现的问题。这仅仅是如何注入特定的实现),我知道使用multibinder绑定多个实例;但是如果它是一个泛型类,则不是...

  1. how could i inject all implementations of the trait "Builder"?? I found a bunch of questions that go in the same direction (using multibinder, TypeLiteral etc. but none of them facing the problem of injecting all implementations. Its just about "how to inject a specific implementation") I know how to bind multiple instances using multibinder; but not if it is a generic class...

最后,我想使用某种外观模式。要注入一个构建器,它将注入所有实现,并且知道需要什么构建器(请参见上面的match-case-fracment)。但是我没有使用区分大小写的东西,而是关注了MapBinder。

In the end i would like to use kind of facade-pattern. having one "builder" to inject, that gets all implementations injected and knows what builder is needed (see match-case-fracment above). But instead of using match-case, i had an eye on the MapBinder. Something like binding the Builder-implementations to a Map, that uses the class as a key.

例如,类似于将Builder实现绑定到Map的事情。 (伪代码)

e.g. (Pseudo-code)

class BuildingComponent @Inject()(builder: Map[Class,Builder]){
  def doIt(item: Any) = {
     builder.get(item.class).build(item)
  }
}


推荐答案

Guise只初始化它知道的类。因此,您可以将所有实现注入到您的外观中,并根据需要对其进行排序。每次添加新的实现时,都需要更改此外观。.

Guice initialize only classes it knows about. So you can inject all implementations into your facade and order them as you want. You will need to change this facade each time you add new implementation.. So not that good..

替代

要动态通知实现有关您的实现,您需要一些反思。如果您可以将 Builder 作为 sealed 特征进行密封,则可以使用标准scala(获取所有可以找到的子类的示例) 此处)或使用第三方库(例如反映)。

To inform guice about your implementations dynamically you need some reflection. You can either use standard scala if you can have your Builder as sealed trait (example of getting all subclasses you can find here) or with thirdparty library (e.g. reflections).

我将解释最后一种情况

您将需要在build.sbt中导入:

You will need imports in your build.sbt:

libraryDependencies ++= Seq(
  "com.google.inject.extensions" % "guice-multibindings" % "<your guice version>",
  "org.reflections" % "reflections" % "0.9.11")

您将需要创建模块并将其通知给guice,例如如果是play框架,则需要放入application.conf

You will need to create module and notify guice about it, e.g. in case of play framework you will need to put in application.conf

play.modules.enabled += "com.example.MyModule"

现在,我假设您能够将所有实现放入同一个包中(您可以查看文档,了解在其他情况下如何获取所有实现)。假设它是 com.example.builders 。同样在我的示例中,我假设您能够将类型参数 T K 移到类型别名中(使用泛型)绑定和注入会有更多技巧-您可以尝试自己找到这种方式)。而您的构建器将是:

Now, I assume that you are able to put all your implementations into same package (you can check docs how to get all implementations in other cases). Let's say it is com.example.builders. Also in my example I assume that you are able to move type parameters T and K into type aliases (with generics it will be a bit more tricks with binding and injecting - you can try to find this way yourself). And your builder will be:

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]

Finally, you can inject builders: java.util.Set[Builder] where you need it.

更新(添加了如何处理类型化实现的示例)

例如:您将需要附加的类来保留砖块输入信息。一旦使用 Builder 特质,我就使用了抽象类

As example: you will need additional class to keep your brick type info. I used abstract class as soon as Builder is trait it should be ok

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)

这篇关于使用guice注入游戏中某个特征/类的所有实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆