I'm trying to use the cake pattern for the first time.
I kind of understand how it works, but would like to know if it is possible to mix already mixed traits or something like that.
What I would like is to build a global application with the cake pattern. And I want another version of that application which would be the same, except at the repository level.
Is it possible to do something like:
trait application extends DefaultUserServiceComponent with MongoUserRepositoryComponent
object realApplication extends application
object fakeApplication extends FakeUserRepositoryComponent with application
I mean: reusing the already built application, when building a fake application using fake repositories?
Short answer: No. You would be inheriting conflicting members. See the following code snippet:
trait Repository {def authenticate(username: String, password: String): String}
trait UserServiceComponent {self: UserRepositoryComponent =>
val userService: UserService = new UserService
class UserService {
def authenticate(username: String, password: String): String =
repository.authenticate(username, password)
}
}
trait UserRepositoryComponent {
def repository: Repository
}
trait MongoUserRepositoryComponent extends UserRepositoryComponent {
val repository: Repository =
new Repository {def authenticate(username: String, password: String) = "MongoAuthed"}
}
trait MockUserRepositoryComponent extends UserRepositoryComponent {
val repository: Repository =
new Repository {def authenticate(username: String, password: String) = "MockAuthed"}
}
trait Application extends UserServiceComponent with MongoUserRepositoryComponent
object RealApplication extends Application
// The following will be an error: "object FakeApplication inherits conflicting members:"
object FakeApplication extends Application with MockUserRepositoryComponent
Instead, to have the desired behavior, define Application as such:
trait Application extends UserServiceComponent {self: UserRepositoryComponent =>}
object RealApplication extends Application with MongoUserRepositoryComponent
object FakeApplication extends Application with MockUserRepositoryComponent
To keep the hierarchy given in the OP, you would need to modify the code as such:
trait MongoUserRepositoryComponent extends UserRepositoryComponent {
private val _repository = new Repository {def authenticate(username: String, password: String) = "MongoAuthed"}
def repository: Repository = _repository
}
trait MockUserRepositoryComponent extends UserRepositoryComponent {
private val _repository = new Repository {def authenticate(username: String, password: String) = "MockAuthed"}
def repository: Repository = _repository
}
trait Application extends UserServiceComponent with MongoUserRepositoryComponent
object RealApplication extends Application
object FakeApplication extends Application with MockUserRepositoryComponent {
override val repository: Repository = super[MockUserRepositoryComponent].repository
}
The additional private val _repository
's are necessary so that we can define repository
as a function, so that it can be used as an override in FakeApplication
. (Using super[type]
to override only works with functions).
EDIT: In the end, the purpose of the cake pattern is to develop a hierarchy where one does not need to override as in the last code snippet I provided. In reality, the trait Application
should not exist at all, only RealApplication
and FakeApplication
. (In the 2nd code snippet I'm essentially doing nothing more than renaming UserServiceComponent
to Application
).