简体   繁体   English

使用Scala进行依赖注入

[英]Dependency injection with Scala

I was searching a way of doing dependency injection in Scala kind of like Spring or Unity in C# and I found nothing really interesting. 我正在寻找一种在Scala中进行依赖注入的方式,比如Spring中的Spring或Unity,我发现没有什么真正有趣的。

  • MacWire: I don't understand the benefit as we have to give the class in wire[CASS]. MacWire:我不明白这个好处,因为我们必须给线上课[CASS]。 So what's the point if you give the implementation when you call wire? 那么,如果在调用wire时给出实现,那又有什么意义呢? I can do new CASS it will be the same. 我可以做新的CASS它会是一样的。
  • Cake pattern with self type: Seems to not answer what I'm searching for. 自我类型的蛋糕模式:似乎没有回答我正在寻找的东西。

So I decided to make my implementation and ask you what do you think because it's surprising me that nothing like this has been done before. 所以我决定进行实施,然后问你的想法是什么,因为令我惊讶的是之前没有做过这样的事情。 Maybe my implementation have lot's of issues in real life also. 也许我的实现在现实生活中也有很多问题。

So here is an example: 所以这是一个例子:

trait Messenger {
  def send
}

class SkypeMessenger extends Messenger {
  def send = println("Skype")
}

class ViberMessenger extends Messenger {
  def send = println("Viber")
}

I want here to inject everywhere in my app the implementation configured in only one place: 我想在我的应用程序中随处注入仅在一个地方配置的实现:

object App {
  val messenger = Inject[Messenger]

  def main(args: Array[String]) {
    messenger.send
  }
}

Note the Inject[Messenger] that I define like below with the config I want (prod or dev): 注意我使用我想要的配置(prod或dev)定义的Inject [Messenger]如下所示:

object Inject extends Injector with DevConfig

trait ProdConfig {
  this: Injector =>
  register[Messager](new SkypeMessager)
  register[Messager](new ViberMessager, "viber")
}

trait DevConfig {
  this: Injector =>
  register[Messager](new ViberMessager)
  register[Messager](new ViberMessager, "viber")
}

And finally here is the Injector which contains all methods apply and register: 最后这里是包含所有方法的Injector并注册:

class Injector {
  var map = Map[String, Any]()

  def apply[T: ClassTag] =
    map(classTag[T].toString).asInstanceOf[T]

  def apply[T: ClassTag](id: String) =
    map(classTag[T].toString + id).asInstanceOf[T]

  def register[T: ClassTag](instance: T, id: String = "") = {
    map += (classTag[T].toString + id -> instance)
    instance
  }
}

To summaries: 总结:

  • I have a class Injector which is a Map between interfaces/traits (eventually also an id) and an instance of the implementation. 我有一个类Injector,它是接口/ traits之间的Map(最终也是id)和实现的实例。
  • We define a trait for each config (dev, prod...) which contains the registers. 我们为包含寄存器的每个配置(dev,prod ...)定义一个特征。 It also have a self reference to Injector. 它还有一个Injector的自我参考。
  • And we create an instance of the Injector with the Config we want 我们用我们想要的Config创建一个Injector实例
  • The usage is to call the apply method giving the Interface type (eventually also an id) and it will return the implementation's instance. 用法是调用apply方法给出Interface类型(最终也是id),它将返回实现的实例。

What do you think? 你怎么看?

You code looks a lot like dependency injection in Lift web framework. 您的代码看起来很像Lift Web框架中的依赖注入。 You can consult Lift source code to see how it's implemented or just use the framework. 您可以查阅Lift源代码以了解它是如何实现的,或者只是使用框架。 You don't have to run a Lift app to use its libraries. 您无需运行Lift应用程序即可使用其库。 Here is a small intro doc . 这是一个小型的介绍文档 Basically you should be looking at this code in Lift: 基本上你应该在Lift中查看这段代码:

package net.liftweb.http

/**
 * A base trait for a Factory.  A Factory is both an Injector and
 * a collection of FactorMaker instances.  The FactoryMaker instances auto-register
 * with the Injector.  This provides both concrete Maker/Vender functionality as
 * well as Injector functionality.
 */
trait Factory extends SimpleInjector

You can also check this related question: Scala - write unit tests for objects/singletons that extends a trait/class with DB connection where I show how Lift injector is used. 您还可以检查以下相关问题: Scala - 使用DB连接扩展特征/类的对象/单例的单元测试,其中显示了如何使用Lift注入器。

Thanks guys, 多谢你们,

So I make my answer but the one from Aleksey was very good. 所以我做出了答案,但是Aleksey的答案非常好。

I understand better the Cake Pattern with this sample: 我更了解这个样本的蛋糕模式:
https://github.com/freekh/play-slick/tree/master/samples/play-slick-cake-sample https://github.com/freekh/play-slick/tree/master/samples/play-slick-cake-sample
Take a look also to the other implementations without DI and compare: 看看没有DI的其他实现并比较:
https://github.com/freekh/play-slick/tree/master/samples/ https://github.com/freekh/play-slick/tree/master/samples/

And so the cake pattern doesn't have a centralized config like we can have with my shown lift style DI. 所以蛋糕模式没有像我们所展示的升降式DI那样的集中配置。 I will anyway use the Cake pattern as it fits well with Slick. 无论如何,我会使用Cake模式,因为它非常适合Slick。
What I didn't like with Subcut is the implicits everywhere. 我对Subcut不喜欢的是各地的暗示。 I know there is a way to avoid them but it looks like a fix to me. 我知道有一种方法可以避免它们,但它看起来像是对我的修复。

Thanks 谢谢

To comment on MacWire , you are right that you could just use new - and that's the whole point :). 要评论MacWire ,你是对的,你可以使用new - 这就是重点:)。 MacWire is there only to let you remove some boilerplate from your code, by not having to enumerate all the dependencies again (which is already done in the constructor). MacWire只允许您从代码中删除一些样板,而不必再次枚举所有依赖项(已在构造函数中完成)。

The main idea is that you do the wiring at "the end of the world", where you assemble your application (or you could divide that into trait-modules, but that's optional). 主要的想法是你在“世界末日”进行布线,在那里组装你的应用程序(或者你可以把它分成特征模块,但这是可选的)。 Otherwise you just use constructors to express dependencies. 否则,您只需使用构造函数来表示依赖关系。 No magic, no frameworks. 没有魔法,没有框架。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM