Вложенные черты шаблона торта

Почему все используют эту форму определения:

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

Вместо этого:

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

trait UserServiceComponent {
  def userService: UserService
}

Второй подход кажется более универсальным, поскольку трейт UserService не привязан к конкретному экземпляру компонента.

Что-то я пропустил?

Редактировать: под «более универсальным» я подразумеваю, что вы можете сделать что-то вроде этого (смешайте два торта с разными импликациями в один):

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 {}
}

Если вы определите UserService как вложенный трейт, вы получите исключение: несоответствие типов; найдено: Program.Cake1.UserService, требуется: Program.UserService


person Dmitry Golubets    schedule 23.03.2014    source источник


Ответы (2)


В вашей второй конструкции население глобального пространства имен удваивается без уважительной причины. В классической конструкции Cake компонент является автономным и вводит только одно имя в глобальное пространство имен.

В любом случае характеристика компонента (здесь trait UserService) одинаково доступна, независимо от того, структурирована она как классический торт или как альтернатива. То есть ваша альтернатива отнюдь не "более универсальна".

person Randall Schulz    schedule 23.03.2014
comment
Рэндалл, я объяснил немного больше, хотя Владимир был быстрее меня :) Итак, нет других причин для вложенности, кроме предотвращения загрязнения пространства имен, верно? - person Dmitry Golubets; 24.03.2014

Ответ Рэндалла Шульца абсолютно правильный. Добавлю только, что иногда вторая форма (с верхним уровнем UserService) неизбежна. Например, когда вам нужно передать компоненты из «внешнего» торта во «внутренний» торт:

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

Если Example было внутренней чертой ExampleComponent, то присваивание

override def example = self.example

было бы невозможно, потому что self.example имеет тип SomeOtherComponent.this.type#Example, а InnerCake.example должен быть типа this.type#Example.

person Vladimir Matveev    schedule 24.03.2014