Scala光滑蛋糕模式:超过9000个课程?

use*_*572 11 scala cake-pattern slick

我正在开发一个Play!2.2使用Slick 2.0在Scala中的应用程序我现在正在处理数据访问方面,试图使用Cake Pattern.它似乎很有希望,但我真的觉得我需要编写一大堆类/特征/对象才能实现非常简单的事情.所以我可以对此有所了解.

用一个User概念的一个非常简单的例子,我理解它的方式是我们应该:

case class User(...) //model

class Users extends Table[User]... //Slick Table

object users extends TableQuery[Users] { //Slick Query
//custom queries
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,这是完全合理的.现在我们添加一个"Cake Patternable" UserRepository:

trait UserRepository {
 val userRepo: UserRepository
 class UserRepositoryImpl {
    //Here I can do some stuff with slick
    def findByName(name: String) = {
       users.withFilter(_.name === name).list
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

然后我们有一个UserService:

trait UserService {
 this: UserRepository =>
val userService: UserService
 class UserServiceImpl { //
    def findByName(name: String) = {
       userRepo.findByName(name)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在我们在一个对象中混合所有这些:

object UserModule extends UserService with UserRepository {
    val userRepo = new UserRepositoryImpl
    val userService = new UserServiceImpl 
}
Run Code Online (Sandbox Code Playgroud)
  1. 是否UserRepository真的有用吗?我可以findByNameUsers光滑的对象中写为自定义查询.

  2. 假设我有另外一组这样的类Customer,我需要使用其中的一些UserService功能.

我应该这样做:

CustomerService {
this: UserService =>
...
}
Run Code Online (Sandbox Code Playgroud)

要么

CustomerService {
val userService = UserModule.userService
...
}
Run Code Online (Sandbox Code Playgroud)

Owe*_*wen 10

好吧,那些听起来像是好目标:

  • 摘要数据库库(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
    }
}
Run Code Online (Sandbox Code Playgroud)

然后CustomerRepository你可以这样做:

trait CustomerRepository { this: UserRepository =>
}

trait SlickCustomerRepository extends CustomerRepository {
}

trait RoughCustomerRepository extends CustomerRepository {
}
Run Code Online (Sandbox Code Playgroud)

并根据您的后端想法组合它们:

object UserModuleWithSlick
    extends SlickUserRepository
    with SlickCustomerRepository

object UserModuleWithRough
    extends RoughUserRepository
    with RoughCustomerRepository
Run Code Online (Sandbox Code Playgroud)

您可以像这样制作可单元测试的对象:

object CustomerRepositoryTest extends CustomerRepository with UserRepository {
    type User = // some mock type
    def findByName(name: String) = {
        // some mock code
    }
}
Run Code Online (Sandbox Code Playgroud)

你是正确的观察到之间有很强的相似性

trait CustomerRepository { this: UserRepository =>
}

object Module extends UserRepository with CustomerRepository
Run Code Online (Sandbox Code Playgroud)

trait CustomerRepository {
    val userRepository: UserRepository
    import userRepository._
}

object UserModule extends UserRepository
object CustomerModule extends CustomerRepository {
    val userRepository: UserModule.type = UserModule
}
Run Code Online (Sandbox Code Playgroud)

这是旧的继承/聚合权衡,为Scala世界更新.每种方法都有优点和缺点.使用混合特征,您将创建更少的具体对象,这可以更容易跟踪(如上所述,您只有一个Module对象,而不是用户和客户的单独对象).在另一方面,性状必须在创建对象的时候混合,所以你不能例如利用现有的UserRepository,并作出CustomerRepository在混合它-如果你需要做到这一点,你必须使用聚集.另请注意,聚合通常需要您指定singleton-types(如above(: UserModule.type)),以便Scala接受路径相关类型相同.该混合特征有另一个强大之处在于它可以处理递归的依赖关系-两者的UserModuleCustomerModule可以提供的东西,需要互相的东西.使用惰性val进行聚合也是可能的,但是在混合特性方面它在语法上更方便.