Reputation: 8043
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
Upvotes: 0
Views: 113
Reputation: 843
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
}
Upvotes: 2