문제

I have a trait DbClientUtil that is as follows:

trait DbClientUtil{
   lazy val dbClient = //Initializing client here
}

I have a Dbhelper object that extends the DbClientUtil trait as follows:

object DbHelper extends DbClientUtil{
   def sendDbPayload(dbModel:DbModel):Unit = {
        dbClient.sendData(dbModel)
   }
}

In the above given example sendData is a method on dbClient.

Now I want to iterate through a collection, convert the elements to a model, send it to the db and then shutdown the client.

So, I write out this strategy in the following object

object Runner extends DbClientUtil{
   import DbHelper._
   List(DbModel("model1"),DbModel("model2"),DbModel("model3")).foreach{
      model => sendDbPayload(model)
   }
   dbClient.shutdown
}

I have two objects that extend the same trait. DBHelper and Runner extend the same trait DbClientUtil. The DbClientUtil initializes the dbClient. The initialized dbClient is being used by both the DbHelper and the Runner. My question is, am I initializing dbClient twice? My next question would be if I am initializing it twice, what would be a better way to rewrite this to prevent any redundant initializations?

Thanks

도움이 되었습니까?

해결책

Actually yes. DbClientUtil initializes twice: first by DbHelper and then by Runner. If this is a problem, there are a lot of ways to solve this:

1) make DbClientUtil stateless and rewrite it as def dbClient. It is actually a good solution, because your state should be stored in a database without any depends on how many clients you have instantiated

2) create singleton. Just one. Not two:

object DbClientUtil extends DbClientUtil

object DbHelper extends DbClientUtil{
  def sendDbPayload(dbModel:DbModel):Unit = {
    DbClientUtil.sendData(dbModel)
  }
}

object Runner {
       List(DbModel("model1"),DbModel("model2"),DbModel("model3")).foreach{
     model => DbHelper.sendDbPayload(model)
  }
  DbHelper.shutdown //Use just one client, not two
}

But actually I don't like this. Strict dependencies gives you more coupling

3) Use dependency injection. I guess this is the one of best solution for large projects. For example, you can use Google Guice:

trait DbClientService {
   def sendData(data: Any): Unit //No implementation
}

class DbClientServiceImpl extends DbClientService
   def sendData(data: Any): Unit {
      //write you implementation code
   }
}

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

class ServiceModule extends AbstractModule {
  protected def configure() {
    bind[DbClientService].to[DbClientServiceImpl].in[Singleton]
  }
}

And then inject it:

class DbHelper @Inject()(dbClient:DbClientService) extends DbClientUtil{
  def sendDbPayload(dbModel:DbModel):Unit = {
    dbClient.sendData(dbModel)
  }
}

class Runner @Inject()(dbClient:DbClientService) extends DbClientUtil{

  List(DbModel("model1"),DbModel("model2"),DbModel("model3")).foreach{
     model => dbClient.sendDbPayload(model)
  }
  dbClient.shutdown 
  //still, don't know if it is good solution 
  //to control client state from outer scope
  //better make this logic private
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top