1

I've read some articles about Scala's cake pattern, basically understood it. Following is some sample code I copied from this article:

Components:

case class User(username:String, password: String)

trait UserRepositoryComponent {
  val userRepository: UserRepository

  class UserRepository {
    def authenticate(user: User): User = { 
      println("authenticating user: " + user)
      user
     }
    def create(user: User) = println("creating user: " + user)
    def delete(user: User) = println("deleting user: " + user)
  }
}


trait UserServiceComponent { this: UserRepositoryComponent =>
  val userService: UserService
  class UserService {
    def authenticate(username: String, password: String): User = 
      userRepository.authenticate(User(username, password))  

    def create(username: String, password: String) = 
      userRepository.create(new User(username, password))

    def delete(user: User) = 
      userRepository.delete(user)

  }
}

Objects to combine them:

object ComponentRegistry extends 
  UserServiceComponent with 
  UserRepositoryComponent {
  val userRepository = new UserRepository
  val userService = new UserService
}

object TestingComponentRegistry extends 
  UserServiceComponent with 
  UserRepositoryComponent {
  val userRepository = mock[UserRepository]
  val userService = mock[UserService]
}

I want to make it simpler if I'm in a simple project. The code will look like:

case class User(username:String, password: String)

class UserRepository {
  def authenticate(user: User): User = { 
    println("authenticating user: " + user)
    user
   }
  def create(user: User) = println("creating user: " + user)
  def delete(user: User) = println("deleting user: " + user)
}

class UserService(userRepository: UserRepository) {
  def authenticate(username: String, password: String): User = 
    userRepository.authenticate(User(username, password))  

  def create(username: String, password: String) = 
    userRepository.create(new User(username, password))

  def delete(user: User) = 
    userRepository.delete(user)

}

Combine them:

object Application {
  val userService = new UserService(new UserRepository)
}

object Test {
  val userService = new UserService(mock[UserRepository]) 
}

My question is, is my code can still be treated as "Dependency injected"?

  1. I declared the dependencies in the UserService's constructor
  2. I combine them with objects in different environments

But I don't provide some trait as "Components".

Freewind
  • 193,756
  • 157
  • 432
  • 708
  • This is the Cake Pattern and a frequent use of it is so-called “dependency injection,” which can in this light be seen as a special case of “componentized” software that is facilitated by Cake. – Randall Schulz Jun 19 '14 at 17:05

1 Answers1

3

Yes your code is "dependency injected": UserService receives the component it is supposed to use as a constructor argument, as opposed to having UserRepository directly instantiated inside UserService.

IMHO, your code is exactly what DI should be 90+% of the time. No need for some "fancy" frameworks like Spring or Guice, no need for some "fancy" design patterns like the cake pattern, just inject your dependencies in the constructor. Done, clean, easily testable, easy to swap out a different implementation (*).

(*): Note that to achieve this in your example UserRepository should probably be a trait instead of a class.

vptheron
  • 7,426
  • 25
  • 34