سؤال

Consider the case where I have an interface that has multiple implementations, each of which is active in my application in a normal deployment. For a more concrete example, we can consider that these are implementations of a Notifier interface, of which there is PushNotifier, EmailNotifier and SmsNotifier.

In Java, using Spring, I inject all 3 into a class, and create a Map<NotificationType, Notifier> that I can use to get the notifier for a certain type of notification.

I'm wondering what the best pattern is to do the same in Scala. Most things I have seen suggest pattern matching:

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

But that seems to sort of defeat the purpose of DI/IoC. However, none of the major Scala DI frameworks provide a great mechanism for having multiple implementations of the same interface injected as a list (actually, I am using Spring in Scala right now - but trying to avoid most of the crazy functionality and use it just for basic wiring)

Is there a more Scala-esque pattern for this that I'm not grasping?

هل كانت مفيدة؟

المحلول

This is not framework- (or lack thereof) dependent, but one Scalish move would be to inject the association as a function instead of as a Map. A Scala Map is also a function.

Given boring Type tokens and instances of T to associate:

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

And an app with a function that uses it:

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

Then inject it as a 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

or a pattern matching anonymous function:

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

actually it's more convenient to use 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

You don't get the type inference with a val, but I seem to remember they wanted to add that.

نصائح أخرى

Have you looked at Guice? It works well in scala too. You could wire up Modules matching your use cases. Notice in the following code (Scala not xml!) that you can define which interface is used for which module.

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

here is an example from scala-guice: in your case you would change the CreditCardPaymentService to a series of NotificationService's. You would put a single "bind" for each of your notification type's

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]
  }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top