I'd like to use the Cake Pattern for splitting parts of some software system into components to make it completely modular as proposed in this article. In the simplest case I'd like to have some mockable components, let's say Logging, Config, Database, Scripts etc which could potentially use each other. The code might look like
trait AbstractConfig {
def config: AbstractConfigInterface
trait AbstractConfigInterface {
def test: Unit
}
}
trait SomeConfig extends AbstractConfig {
this: Core =>
def config = SomeConfigImplementation
object SomeConfigImplementation extends AbstractConfigInterface {
def test = println("conf.test method called")
}
}
trait AbstractDatabase {
def database: AbstractDatabaseInterface
trait AbstractDatabaseInterface {
def connect: Unit
}
}
trait SomeDatabase extends AbstractDatabase {
this: Core =>
def database = SomeDatabaseImplementation
object SomeDatabaseImplementation extends AbstractDatabaseInterface {
def connect = {
println("connect method called")
core.conf.test
}
}
}
trait Core {
this: AbstractDatabase with AbstractConfig =>
def core = CoreInterface
object CoreInterface {
def db = database
def conf = config
}
}
object app extends Core with SomeDatabase with SomeConfig
object Run {
def main(args: Array[String]) = {
app.core.db.connect
}
}
Here the database and config components (SomeConfig
and SomeDatabase
traits) are pluggable and can be changed to some other implementations if ever needed. Their implementations have access to core
object which holds both database and config, so the database can access config if needed and vice versa.
So the question is: If ever some trait like SomeDatabase
becomes large and doesn't fit into a single file how to split it into separate classes retaining access to the core
object? To be more specific, let's say I need to move some code out of connect method in SomeDatabase
to another file:
// SomeDatabase.scala
trait SomeDatabase extends AbstractDatabase {
this: Core =>
def database = SomeDatabaseImplementation
object SomeDatabaseImplementation extends AbstractDatabaseInterface {
def connect = {
val obj = new SomeClass()
}
}
}
// SomeClass.scala in the same package
class SomeClass {
core.conf.test // Does not compile - how to make it work??
}
SomeClass
is implementation details of how SomeDatabase works, so I obviously wouldn't like to make it a trait and mix it in to application. Is there any way to provide access to core
object for SomeClass
?
Some related links:
The simplest thing to do would be to pass Core
in as a constructor parameter to SomeClass
.
// SomeDatabase.scala
trait SomeDatabase extends AbstractDatabase {
this: Core =>
def database = SomeDatabaseImplementation
object SomeDatabaseImplementation extends AbstractDatabaseInterface {
def connect = {
val obj = new SomeClass(SomeDatabase.this) // pass it here
}
}
}
// SomeClass.scala in the same package
class SomeClass(coreComp: Core) { // use it here
coreComp.core.conf.test
}
Interestingly, I really just wanted to pass CoreInterface
or AbstractConfigInterface
, but the fact that they are inner types really made that difficult.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With