简体   繁体   中英

DI in Scala with Cake Pattern

This article explains Dependency Injection via Scala's Cake Pattern .

My understanding of this pattern's benefit is that traits can be mixed in (production v. test) with static checking.

In Mr. Bonér's example, he lists this finished (per example) code:

UserRepositoryComponent and UserServiceComponent

I added comments per my understanding.

trait UserRepositoryComponent {
  val userRepository: UserRepository    // stand-alone component

  class UserRepository {      
    ...                      // actual implementation here
  }
} 
trait UserServiceComponent { 
  this: UserRepositoryComponent =>      //Requires a mixed-in UserRepo*Component

  val userService: UserService  

  class UserService {
    ...                      // actual implementation here
  }
}

My understanding is that the Service depends on injection of a Repository component.

For production purposes, the following can be used to wire a "production" Repository component into the UserServiceComponent :

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

If our production code wanted to use the userRepository or userService , is the correct way to use them via a simple import ?

I think that I understand half of the article up to this point, but I'm not sure how to use the ComponentRegistry object.

You're running head first into the bakery of doom bro: What are some compelling use cases for dependent method types?

To answer your question, the proper way to use userService would be to use another trait and cake it up:

trait Example { this: UserServiceComponent => 
   def getExampleUser() = userService.getUser("ExampleUser")
}

Now whatever this new trait does isn't directly coupled to anything like the object ComponentRegistry . Instead your application becomes this:

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

Anyway, you should run for the hills because if you really want to use cake you should be doing something more like this:

trait UserRepositoryComponent {

  type UserRepository <: UserRepositoryLike

  val userRepository: UserRepository

  trait UserRepositoryLike {
    def getUserOrSomething()
  }

}

trait UserRepositoryComponentImpl extends UserRepositoryComponent {

  type UserRepository = UserRepositoryImpl
  val userRepository = new UserRepositoryImpl

  class UserRepositoryImpl extends UserRepositoryLike {
    override def getUserOrSomething() = ???
  }

}

trait UserServiceComponent {
  this: UserRepositoryComponent =>

  type UserService <: UserServiceLike
  val userService: UserService

  trait UserServiceLike {
    def getUserNameById(id: Int): String
  }

}

trait UserServiceComponentImpl extends UserServiceComponent {
  this: UserRepositoryComponent =>

  type UserService = UserServiceImpl
  val userService = new UserServiceImpl

  class UserServiceImpl extends UserServiceLike {
    override def getUserNameById(id: Int) = userRepository.getUserOrSomething
  }

}

trait Example {
  this: UserServiceComponent =>

  def getExampleUser() = userService.getUserNameById(1)

}

object Application extends
Example with
UserRepositoryComponentImpl with
UserServiceComponentImpl

Now save yourself some time, drop the cake pattern, and do something simple .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM