Шаблон торта: один компонент на реализацию или один компонент на черту?
В настоящее время я работаю над тем, чтобы использовать шаблон торта в своем приложении.
На примерах, которые я обнаружил в Интернете, они являются базовыми, но не связаны с более сложными потребностями. То, что я хотел бы сделать, не так уж и необычно: я хотел бы, чтобы внутри приложения с шаблоном тортов было 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
реализация.