WalzoneInterview Prep
📞 Interviewing soon? Practice with a realistic AI mock phone interview — it calls you, then scores you. First 15 min FREE →

Scala · Advanced · question 43 of 100

How can you use the Cake Pattern for dependency injection in Scala? Provide an example.?

📕 Buy this interview preparation book: 100 Scala questions & answers — PDF + EPUB for $5

The Cake Pattern is a design pattern in Scala for dependency injection that relies on mixing traits and self-type annotations to build modular and composable applications. The main idea is to define components as traits with abstract dependencies and instantiate the whole structure in a single place.

Here is a step-by-step explanation of how you can use the Cake Pattern for dependency injection in Scala:

1. Define components as traits with abstract dependencies (using ‘val‘ or ‘def‘ declarations).

2. Provide concrete implementations of dependencies for each component.

3. Use self-type annotations to specify the dependencies required by each trait.

4. Combine traits together to create the final application instance.

Consider an example of a simple application with two services: ‘UserService‘ and ‘LoggingService‘. The aim is to inject the ‘LoggingService‘ into the ‘UserService‘.

Step 1: Define components as traits with abstract dependencies.

trait LoggingService {
  def log(message: String): Unit
}

trait UserService {
  self: LoggingService => // This is a self-type annotation, declaring a dependency on LoggingService

  def createUser(username: String): Unit = {
    log(s"Creating user: $username")
    // ... logic to create a user ...
  }
}

Step 2: Provide concrete implementations of dependencies for each component.

trait ConsoleLoggingService extends LoggingService {
  override def log(message: String): Unit = println(message)
}

trait FileLoggingService extends LoggingService {
  override def log(message: String): Unit = {
    // ... Write log message to a file ...
  }
}

Step 3: Use self-type annotations to specify the dependencies required by each trait (already done in Step 1).

Step 4: Combine traits together to create the final application instance.

object Application extends App {
  val userServiceWithConsoleLogging = new UserService with ConsoleLoggingService

  userServiceWithConsoleLogging.createUser("johndoe")
}

In this example, we defined ‘UserService‘ and ‘LoggingService‘ traits and specified the dependency between them using a self-type annotation. Then we provided two implementations of ‘LoggingService‘: ‘ConsoleLoggingService‘ and ‘FileLoggingService‘. Finally, we instantiated the ‘UserService‘ with ‘ConsoleLoggingService‘ by mixing traits together.

This way, using the Cake Pattern, we were able to inject the ‘LoggingService‘ into the ‘UserService‘ in a flexible and modular manner. Different implementations of the dependencies can be easily swapped or combined, and adding new components or dependencies becomes quite manageable.

However, it is essential to note that the Cake Pattern is not as popular as it used to be. Modern Scala developers often prefer more lightweight and simpler alternatives for dependency injection, like constructor-based injection or libraries like MacWire or Google Guice.

Reading is step one. Saying it out loud is the interview. Our AI interviewer calls your phone and runs a realistic Scala interview — then scores it.
📞 Practice Scala — free 15 min
📕 Buy this interview preparation book: 100 Scala questions & answers — PDF + EPUB for $5

All 100 Scala questions · All topics