简体   繁体   中英

Scala - Dependency injection in companion objects

I am from Java background and new to functional paradigm as such, so please forgive and correct me I smell like OOP.

I have a trait names PaymentHandler

trait PaymentHandler {

def handleInit(paymentInfo: PaymentInfo, paymentConfig: PaymentConfig): Either[Future[WSResponse], Exception]

def handleComplete(paymentinfo: PaymentInfo, paymentConfig: PaymentConfig, clientToken: String): Future[Map[String, String]]

}

and

@Singleton
class PayPal extends PaymentHandler{....}
@Singleton
class BrainTree extends PaymentHandler{....}

Till here everything is Ok. but the problem comes when I want to have multiple PaymentGateways based on Payment method. Initially I wrote a PaymentHandlerFactory like

class PaymentHandlerFactory @Inject()(
    paypal:PayPal,
    braintree:BrainTree) {

  def get(name:String): PaymentHandler = {
     name match {
       case "paypal" => paypal
       case "bt" => braintree
       case _ => null
     }
  }
}

and then use this PaymentHandlerFactory to get the PayPal object injected by guice. But somehow I felt this is not right way to do and I got onto companion objects. so this is the code I wrote then

object PaymentHandler {
  @Inject
  val paypal:PayPal = null

  def apply(name:String): PaymentHandler = {
    name match {
      case "paypal" => paypal
      case _ => null
    }
  }
}

Ofcourse this fails as it cannot Inject PayPal object. Now I have two questions in my mind.

  1. Why we cannot inject in companion objects in scala?
  2. What is the right way to implement such factory like things in scala?
  1. The reason you cannot inject in companion object is because they are objects rather than classes. This means only a single instance exists and that is created by scala itself. If you want to do dependency injection, then the thing that requires the dependency must be created by the dependency injection framework. Therefore, you need to model your fatory using a class if you want to use guice.

  2. I think what you did with your class PaymentHandlerFactory is the pragmatic way and probably works in most cases. I would refrain from prematurely making this too generic.

If you want to make it a bit more guice-y, you could use their AssistedInject .

If you land up on this question about DI on companion objects, please check the below workaround

  • using a corresponding class with DI
  • create a companion object on this class

The benefit of DI is that it will be propagated to this companion object.

If you have a companion object on class Paypal and class BrainTree as below:

@Singleton
class PayPal extends PaymentHandler{....}
object PayPal extends PayPal
@Singleton
class BrainTree extends PaymentHandler{....}
object BrainTree extends BrainTree

Now, I can use these objects in the PaymentHandler companion object as below:

// get the correct handler from factory
class PaymentHandler extends PaymentHandlerFactory(PayPal, BrainTree){...}
object PaymentHandler extends PaymentHandler

Now in any class, we can use PaymentHandler.get()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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