I'm trying to follow the example from this blog. I understand the example but having trouble implementing it.
trait Database {
// ...
}
trait UserDb {
this: Database =>
// ...
}
trait EmailService {
this: UserDb =>
// Can only access UserDb methods, cannot touch Database methods
}
The example mentions that the full Database functionality will be hidden from the EmailService - this is what i'm after but don't know how to implement these traits correctly.
This is what i tried to implement:
trait Database {
def find(query: String): String
}
trait UserDb {
this: Database =>
}
trait EmailService {
this: UserDb =>
}
trait MongoDatabase extends Database {
}
trait MongoUserDb extends UserDb with MongoDatabase{
}
class EmailServiceImpl extends EmailService with MongoUserDb {
override def find(query: String): String = {
"result"
}
}
It looks weird to me becasue MongoDatabase trait didn't asked for find
implementation and when i implemented EmailService
i was then prompted for find
implementation,although the example mentioned this will be hidden from the EmailService
. What am i missing here?
After reading your comments, I'm trying to implement what i'm being trying to understand on an example that is closer to what i'm actually trying to do.
The first snippet won't compile, but the second one will...
At the end of the day i want to have different Repository
implementations where i can switch between the Databases they rely on, am i close with one of the snippets below?
trait Database {
def find(s: String): String
}
trait Repository {
this: Database =>
}
class UserRepository extends Repository {
def database = new MongoDB
class MongoDB extends Database {
def find(s: String): String = {
"res"
}
}
}
trait Repository {
def database: Database
trait Database {
def find(s: String): String
}
}
trait UserRepository extends Repository {
def database = new MongoDB
class MongoDB extends Database {
def find(s: String): String = {
"res"
}
}
}
As mentioned MongoUserDB
will not ask for an implementation as its a trait
. However since EmailServiceImpl
extends
the trait it needs to provide an implementation.
What you are looking for could be done by adding another abstraction. I do it using the service
and DAO
architecture.
Below is a working example that you may use to see if it suits you.
//All future versions of DAO will extend this
trait AbstractDAO{
def getRecords:String
def updateRecords(records:String):Unit
}
//One concrete version
trait concreteDAO extends AbstractDAO{
override def getRecords={"Here are DB records"}
override def updateRecords(records:String){
//Actual DB calls and operations
println("Updated "+records)
}
}
//Second concrete version
trait concreteDAO1 extends AbstractDAO{
override def getRecords={"DB Records returned from DAO2"}
override def updateRecords(records:String){
//Actual DB calls and operations
println("Updated via DAO2"+records)
}
}
//This trait just defines dependencies (in this case an instance of AbstractDAO) and defines operations based over that
trait service{
this:AbstractDAO =>
def updateRecordsViaDAO(record:String)={
updateRecords(record)
}
def getRecordsViaDAO={
getRecords
}
}
//Test Stub
object DI extends App{
val wiredObject = new service with concreteDAO //injecting concrete DAO to the service and calling methods
wiredObject.updateRecords("RECORD1")
println(wiredObject.getRecords)
val wiredObject1 = new service with concreteDAO1
wiredObject1.updateRecords("RECORD2")
println(wiredObject1.getRecords)
}
EDIT ---
Here is the code you might want to implement,
trait Database {
def find(s: String): String
}
trait MongoDB extends Database{
def find(s:String):String = { "Test String" }
}
trait SQLServerDB extends Database{
def find(s:String):String = { "Test String2" }
}
trait Repository {
this: Database =>
}
class UserRepository extends Repository with MongoDB{ // UserRepository is injected with MongoDB here
find("call MongoDB") //This call will go to the find method in MongoDB trait
}
class UserRepository1 extends Repository with SQLServerDB{ // UserRepository is injected with SQLServerDB here
find("call SQLServerDB") //This call will go to the find method in SQLServerDB trait
}