简体   繁体   中英

Cake pattern nested traits

Why everyone is using this form of definition:

trait UserServiceComponent {
  def userService: UserService
  trait UserService {
    def findAll: List[User]
    def save(user: User)
  }
}

Instead of that:

trait UserService {
  def findAll: List[User]
  def save(user: User)
}

trait UserServiceComponent {
  def userService: UserService
}

Second approach seems more universal because UserService trait is not bound to specific component instance.

Anything I'm missing?

Edit: By "more universal" I mean that you can do something like this (mix two cakes with different impl. into one):

object Program extends App with UserServiceComponent with ProductServiceComponent  {
    object Cake1 extends UserServiceComponentImpl with DatabaseComponent1Impl {
    }
    object Cake2 extends ProductServiceComponentImpl with DatabaseComponent2Impl {
    }

    def userService = Cake1.userService
    def productService = Cake2.productService
}

// def
trait DatabaseComponent{
}

trait UserService {
}

trait UserServiceComponent {
  def userService: UserService
}

trait ProductService {
}

trait ProductServiceComponent {
  def productService: ProductService
}

// impl
trait DatabaseComponent1Impl extends DatabaseComponent {
}

trait DatabaseComponent2Impl extends DatabaseComponent {
}

trait UserServiceComponentImpl extends UserServiceComponent {
    self:DatabaseComponent =>

    def userService = new UserService {}
}

trait ProductServiceComponentImpl extends ProductServiceComponent {
    self:DatabaseComponent =>

    def productService = new ProductService {}
}

If you define UserService as nested trait then you'll get an exception: type mismatch; found: Program.Cake1.UserService, required: Program.UserService

In your second construction, the population of the global namespace is doubled for no good reason. With the classic Cake construction a component is self-contained and it injects exactly one name into the global namespace.

In either event, the characteristic of the component (here trait UserService ) is equally available whether structured as classic Cake or your alternative. That is, your alternative is by no means "more universal."

Randall Schulz' answer is absolutely correct. I'd only add that sometimes the second form (with top-level UserService ) is inevitable. For example, when you have to pass components from "outer" cake to "inner" cake:

trait ExampleComponent {
    def example: Example
}

trait Example {
    def doSomething()
}

trait DefaultExampleComponent {
    override val example = new Example {
        // ...
    }
}

trait SomeOtherComponent { self: ExampleComponent =>
    object InnerCake extends ExampleComponent {
        override def example = self.example
    }
}

object TopLevelCake extends DefaultExampleComponent with SomeOtherComponent

If Example was an inner trait of ExampleComponent , then assignment

override def example = self.example

wouldn't be possible, because self.example has type SomeOtherComponent.this.type#Example but InnerCake.example should be of type this.type#Example .

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