Ответ 1
ОК, это звучит как хорошие цели:
- Аннотация по библиотеке базы данных (slick,...)
- Проверить работоспособность блока признаков
Вы можете сделать что-то вроде этого:
trait UserRepository {
type User
def findByName(name: String): User
}
// Implementation using Slick
trait SlickUserRepository extends UserRepository {
case class User()
def findByName(name: String) = {
// Slick code
}
}
// Implementation using Rough
trait RoughUserRepository extends UserRepository {
case class User()
def findByName(name: String) = {
// Rough code
}
}
Тогда для CustomerRepository
вы можете сделать:
trait CustomerRepository { this: UserRepository =>
}
trait SlickCustomerRepository extends CustomerRepository {
}
trait RoughCustomerRepository extends CustomerRepository {
}
И объедините их на основе ваших прихотей:
object UserModuleWithSlick
extends SlickUserRepository
with SlickCustomerRepository
object UserModuleWithRough
extends RoughUserRepository
with RoughCustomerRepository
Вы можете сделать объекты, подлежащие тестированию:
object CustomerRepositoryTest extends CustomerRepository with UserRepository {
type User = // some mock type
def findByName(name: String) = {
// some mock code
}
}
Вы правильно заметили, что существует сильное сходство между
trait CustomerRepository { this: UserRepository =>
}
object Module extends UserRepository with CustomerRepository
и
trait CustomerRepository {
val userRepository: UserRepository
import userRepository._
}
object UserModule extends UserRepository
object CustomerModule extends CustomerRepository {
val userRepository: UserModule.type = UserModule
}
Это старый компромисс между наследованием и агрегацией, обновленный для мира Scala. Каждый подход имеет свои преимущества и недостатки. С помощью признаков смешивания вы создадите меньше конкретных объектов, что может быть проще отслеживать (как в приведенном выше примере, у вас есть только один объект Module
, а не отдельные объекты для пользователей и клиентов). С другой стороны, черты должны быть смешаны во время создания объекта, поэтому вы не могли, например, взять существующий UserRepository
и сделать CustomerRepository
, смешав его, если вам нужно это сделать, вы должны использовать агрегацию, Также обратите внимание, что для агрегации часто требуется указать одноэлементные типы, например, выше (: UserModule.type
), чтобы Scala согласился с тем, что зависимые от пути типы являются одинаковыми. Другая сила, которая имеет свойства смешивания, состоит в том, что она может обрабатывать рекурсивные зависимости - как UserModule
, так и CustomerModule
могут обеспечить что-то и что-то требовать друг от друга. Это также возможно при агрегировании с использованием ленивых vals, но это более синтаксически удобно со смесями.