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


我正在开发一个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 其中的功能。

我应该这样做:

CustomerService {
this: UserService =>
...
}

要么

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

10216
2018-04-04 15:02


起源

从哪里获得9000? - Blankman
@Blankman knowyourmeme.com/memes/its-over-9000 - user1498572


答案:


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

  • 摘要数据库库(光滑,...)
  • 使性状单元可测试

你可以这样做:

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 可以提供一些东西,并要求彼此的东西。使用惰性val进行聚合也是可能的,但是在混合特性方面它在语法上更方便。


10
2018-04-04 20:33



谢谢你的回答可能已经更好了。我想要的一个抽象是能够在需要时从光滑切换到其他东西,尽可能少的变化。此外,我希望能够轻松地测试所有这些,这也是我想到这种模式有助于模拟类的原因。 - user1498572
感谢您提供了很好的解释和示例,现在它更加清晰。单例类型和递归依赖也是我遇到的问题所以它也有助于此。混淆特性方法让我感到困扰的是模块很快就包含了大量的存储库/服务/什么,因为所有的混合依赖项,所以当你使用模块时,你最终可以访问所有这些“抵押品”课虽然你不一定需要/想要。 - user1498572


看看我最近发表的 光滑的架构备忘单。它没有抽象出数据库驱动程序,但是通过这种方式改变它是微不足道的。把它包起来

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

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

如果每个数据库表的重复语法开销困扰您,请生成代码。该 光滑的代码生成器是可定制的正是为了这个目的:

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

要通过其他方式替换Slick,请使用其他库将查询替换为DAO方法。


4
2018-04-06 10:47



好的模板,你可能是正确的,对于这个特殊的情况,蛋糕模式可能是一个矫枉过正,但这是我为简单起见的一个基本的例子,应用程序实际上更大,可以使用一些模块化。我尝试了代码生成器,这很好,但是有一些副作用问题,比如播放演变,我知道有解决方案,但我还不想进入。另外,我喜欢将Business模型类(案例类User)与任何DAO依赖项完全分开,并将它们放在单独的文件中。但我会将你的模板用于光滑的部分。 - user1498572
是的,我们需要提供光滑的代码生成和播放(或光滑)演变的开箱即用的集成。这方面有这方面的工作: blog.papauschek.com/2013/12/... - cvogt
是的我看到它看起来很有希望。我可能会在它稳定并且应用程序变得太大时结束使用,但是现在我拥有的模型类的数量不是很大并且可以手动管理,而且它帮助我真正理解我正在做的事情: ) - user1498572