【问题标题】:Scala Multiple Implementations DesignScala 多重实现设计
【发布时间】:2014-05-11 02:56:51
【问题描述】:

考虑这样一种情况,我有一个具有多个实现的接口,每个实现在我的应用程序中的正常部署中都是活动的。举个更具体的例子,我们可以认为这些是Notifier接口的实现,其中有PushNotifierEmailNotifierSmsNotifier

在 Java 中,使用 Spring,我将所有 3 个注入到一个类中,并创建一个 Map<NotificationType, Notifier>,我可以使用它来获取特定类型通知的通知器。

我想知道在 Scala 中做同样事情的最佳模式是什么。我见过的大多数事情都暗示了模式匹配:

notificationType match {
    case Push => pushNotifier.notify
    case Email => emailNotifier.notify
    // ...
}

但这似乎有点违背了 DI/IoC 的目的。然而,没有一个主要的 Scala DI 框架提供了一个很好的机制来将同一接口的多个实现作为一个列表注入(实际上,我现在在 Scala 中使用 Spring - 但试图避免大多数疯狂的功能并只使用它用于基本接线)

有没有我没有掌握的更类似于 Scala 的模式?

【问题讨论】:

  • 也许这是因为我从未使用过 DI 框架,但我不清楚最终目标是什么。为什么需要测试这是哪种类型的通知器?为什么不让它成为一个黑盒特征呢?
  • @Owen 我有多个 trait 实现,每个都处理不同类型的通知。还有其他方法可以处理这个问题,但使用地图是性能最高的方法(而不是使用 supports 方法或我在每次迭代中调用的类似方法)

标签: scala dependency-injection


【解决方案1】:

这不依赖于框架(或缺乏框架),但 Scalish 的一个举措是将关联作为函数而不是 Map 注入。 Scala Map 也是一个函数。

给定无聊的 Type 标记和要关联的 T 实例:

scala> object Types extends Enumeration { val AType, BType, CType = Value }
defined object Types

scala> import Types._
import Types._

scala> trait T { def t: String }
defined trait T

scala> case class A(t: String = "a") extends T
defined class A

scala> case class  B(t: String = "b") extends T
defined class B

scala> case class  C(t: String = "c") extends T
defined class C

还有一个具有使用它的功能的应用:

scala> trait AnApp { val types: Value => T ; def f(t: Value) = types(t).t }
defined trait AnApp

然后将其作为 Map 注入:

scala> object MyApp extends AnApp { val types = Map(AType -> A("a1"), BType -> B(), CType -> C()) }
defined object MyApp

scala> MyApp f BType
res0: String = b

或模式匹配匿名函数:

scala> object AnotherApp extends AnApp { val types: Value => T = {
     | case AType => A("a2") case BType => B() case CType => C() } }
defined object AnotherApp

scala> AnotherApp f CType
res1: String = c

其实用def更方便:

scala> trait AnApp { def types: Types.Value => T ; def f(t: Types.Value) = types(t).t }
defined trait AnApp

scala> object AnyApp extends AnApp { def types = {
     | case AType => A("a2") case BType => B() case CType => C() } }
defined object AnyApp

你没有得到带有 val 的类型推断,但我似乎记得他们想添加它。

【讨论】:

    【解决方案2】:

    你看过古斯吗?它在 scala 中也很有效。您可以连接与您的用例匹配的模块。请注意,在以下代码(Scala 不是 xml!)中,您可以定义哪个接口用于哪个模块。

    https://github.com/codingwell/scala-guice/

    这是 scala-guice 的一个示例:在您的情况下,您可以将 CreditCardPaymentService 更改为一系列 NotificationService。您将为每种通知类型添加一个“绑定”

    class MyModule extends AbstractModule with ScalaModule {
      def configure {
        bind[Service].to[ServiceImpl].in[Singleton]
        bind[CreditCardPaymentService]
        bind[Bar[Foo]].to[FooBarImpl]
        bind[PaymentService].to[CreditCardPaymentService]
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-19
      • 1970-01-01
      • 2018-06-04
      • 1970-01-01
      • 1970-01-01
      • 2011-01-16
      相关资源
      最近更新 更多