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

时间:2014-04-04 15:02:24

标签: scala slick cake-pattern

我正在开发一个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
}

到目前为止,这是完全合理的。现在我们添加一个“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
    }
  }
}

然后我们有UserService

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

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

object UserModule extends UserService with UserRepository {
    val userRepo = new UserRepositoryImpl
    val userService = new UserServiceImpl 
}
  1. UserRepository真有用吗?我可以在findByName光滑对象中将Users写为自定义查询。

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

  3. 我应该这样做:

    CustomerService {
    this: UserService =>
    ...
    }
    

    CustomerService {
    val userService = UserModule.userService
    ...
    }
    

2 个答案:

答案 0 :(得分: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
    }
}

然后对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接受路径相关类型相同。混合特征的另一个强大功能是它可以处理递归依赖性 - UserModuleCustomerModule都可以提供某些东西并且需要彼此之间的某些东西。使用惰性值进行聚合也可以实现这一点,但在混合特征方面,语法更方便。

答案 1 :(得分:4)

查看我最近发布的Slick architecture cheat sheet。它不会抽象数据库驱动程序,但是通过这种方式改变它是微不足道的。只需将其包裹在

class Profile(profile: JdbcProfile){
  import profile.simple._
  lazy val db = ...
  // <- cheat sheet code here
}

你不需要蛋糕图案。只需将其全部放在一个文件中即可,无需使用它。如果您愿意支付语法开销,则蛋糕模式允许您将代码拆分为不同的文件。人们还使用蛋糕模式创建不同的配置,包括不同的服务组合,但我不认为这与您相关。

如果每个数据库表的重复语法开销困扰您,请生成代码。 Slick code-generator is customizable完全是出于此目的:

如果要混合手写和生成的代码,可以将手写代码提供给代码生成器,也可以使用方案,生成的代码继承自手写,反之亦然。

要用其他方法替换Slick,请使用其他库替换DAO方法。