Шаблон торта: один компонент на реализацию или один компонент на черту?

В настоящее время я работаю над тем, чтобы использовать шаблон торта в своем приложении.

На примерах, которые я обнаружил в Интернете, они являются базовыми, но не связаны с более сложными потребностями. То, что я хотел бы сделать, не так уж и необычно: я хотел бы, чтобы внутри приложения с шаблоном тортов было 2 сервиса одного типа, использующих разные реализации.

trait UserServiceComponent {
  self: UserRepositoryComponent =>
  val userService: UserService

  class DefaultUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = userRepository.getPublicProfile(id)
  }

  class AlternativeUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = call webservice here for exemple...
  }
}

trait UserService extends RepositoryDelegator[User] {
  def getPublicProfile(id: String): Either[Error, User]
}

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

Должен ли я создать 2 отдельных компонента? Каждый из них выставляет свое имя для userService? (DefaultUserService/alternativeUserService). Использование одного компонента для обеих реализаций. Я не знаю, как другие компоненты смогут узнать, какая реализация используется при использовании имени. userService так как в моем приложении есть 2 разных реализации.

Кстати, так как компонент выражает зависимость от UserRepositoryComponentв то время как это не требуется во всех реализациях, я нахожу немного странным иметь только один компонент, верно? Представьте, что я не хочу создавать полное приложение, которое нуждается в обеих реализациях, но мне нужно, для тестов, чтобы построить только AlternativeUserService, который не нуждается в UserRepositoryComponentбыло бы странно предоставлять эту зависимость, поскольку она не будет использоваться.

Может кто-нибудь дать мне несколько советов, чтобы я знал, что делать?

Вид связанного вопроса: Шаблон торта: как получить все объекты типа UserService, предоставляемые компонентами

Спасибо

1 ответ

Решение

Перво-наперво, вы должны отделить UserServiceComponent от реализации UserService:

trait UserService extends RepositoryDelegator[User] {
  def getPublicProfile(id: String): Either[Error, User]
}

trait UserServiceComponent {
  val userService: UserService
}

trait DefaultUserServiceComponent extends UserServiceComponent { self: UserRepositoryComponent =>
  protected class DefaultUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = userRepository.getPublicProfile(id)
  }
  val userService: UserService = new DefaultUserService
}

trait AlternativeUserServiceComponent extends UserServiceComponent {
  protected class AlternativeUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = call webservice here for exemple...
  }
  val userService: UserService = new AlternativeUserService
}

Если это выглядит многословно, ну, это так. Пирожное не очень лаконично.

Но обратите внимание, как это решает вашу проблему с зависимостью от UserRepositoryComponent даже когда фактически не требуется (например, когда используется только AlternativeUserService).

Теперь все, что нам нужно сделать при создании экземпляра приложения, это смешать DefaultUserServiceComponent или же AlternativeUserServiceComponent,

Если вам необходим доступ к обеим реализациям, вам действительно следует предоставить два имени значения userService. Ну на самом деле, 3 имени, такие как:

  • defaultUserService для DefaultUserService реализация
  • alternativeUserService для AlternativeUserService реализация
  • mainUserService для любого UserService реализация (приложение выбирает, какое из них во время смешивания).

К примеру:

trait UserService extends RepositoryDelegator[User] {
  def getPublicProfile(id: String): Either[Error, User]
}

trait MainUserServiceComponent {
  val mainUserService: UserService
}

trait DefaultUserServiceComponent { self: UserRepositoryComponent =>
  protected class DefaultUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = userRepository.getPublicProfile(id)
  }
  val defaultUserService: UserService = new DefaultUserService
}

trait AlternativeUserServiceComponent {
  protected class AlternativeUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = ??? // call webservice here for exemple...
  }
  val alternativeUserService: UserService = new AlternativeUserService
}

Тогда вы можете создать свой торт так:

object MyApp 
  extends MainUserServiceComponent 
  with DefaultUserServiceComponent 
  with AlternativeUserServiceComponent 
  with MyUserRepositoryComponent // Replace with your real UserRepositoryComponent here    
{
  //val userService = defaultUserService
  val mainUserService = alternativeUserService
}

В приведенном выше примере сервисы, которые явно хотят получить доступ к DefaultUserService поставил бы DefaultUserServiceComponent в зависимости от их компонента (то же самое для AlternativeUserService а также AlternativeUserServiceComponent) и услуги, которые просто нужны UserService вместо этого поставил бы MainUserServiceComponent как зависимость. Вы решаете в "смешанное время", какой сервис mainUserService указывает на (здесь, это указывает на DefaultUserService реализация.

Другие вопросы по тегам