Scala Slick Cake Pattern:超过 9000 个类? [英] Scala Slick Cake Pattern: over 9000 classes?

查看:29
本文介绍了Scala Slick Cake Pattern:超过 9000 个类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个 Play!2.2 在 Scala 中使用 Slick 2.0 的应用程序,我现在正在处理数据访问方面,尝试使用 Cake Pattern.看起来很有希望,但我真的觉得我需要编写大量的类/特征/对象来实现一些非常简单的事情.所以我可以稍微了解一下.

I'm developing a Play! 2.2 application in Scala with Slick 2.0 and I'm now tackling the data access aspect, trying to use the Cake Pattern. It seems promising but I really feel like I need to write a huge bunch of classes/traits/objects just to achieve something really simple. So I could use some light on this.

举一个带有User概念的非常简单的例子,我的理解是我们应该有:

Taking a very simple example with a User concept, the way I understand it is we should have:

case class User(...) //model

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

object users extends TableQuery[Users] { //Slick Query
//custom queries
}

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

So far it's totally reasonable. Now we add a "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)
    }
  }
}

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

Now we mix all of this in an object :

object UserModule extends UserService with UserRepository {
    val userRepo = new UserRepositoryImpl
    val userService = new UserServiceImpl 
}

  1. UserRepository 真的有用吗?我可以在 Users 光滑对象中编写 findByName 作为自定义查询.

  1. Is UserRepository really useful? I could write findByName as a custom query in Users slick object.

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

Let's say I have another set of classes like this for Customer, and I need to use some UserService features in it.

我应该这样做:

CustomerService {
this: UserService =>
...
}

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

推荐答案

好吧,这些目标听起来不错:

OK, those sound like good goals:

  • 对数据库库进行抽象(光滑,...)
  • 使特征单元可测试

你可以这样做:

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 你可以这样做:

Then for CustomerRepository you could do:

trait CustomerRepository { this: UserRepository =>
}

trait SlickCustomerRepository extends CustomerRepository {
}

trait RoughCustomerRepository extends CustomerRepository {
}

并根据您的后端奇思妙想组合它们:

And combine them based on your backend whims:

object UserModuleWithSlick
    extends SlickUserRepository
    with SlickCustomerRepository

object UserModuleWithRough
    extends RoughUserRepository
    with RoughCustomerRepository

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

You can make unit-testable objects like so:

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

您正确地观察到两者之间存在很强的相似性

You are correct to observe that there is a strong similarity between

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 都可以相互提供一些东西,也可以从彼此那里获取一些东西.这也可以通过使用惰性 val 的聚合来实现,但使用混合特征在语法上更方便.

This is the old inheritance/aggregation tradeoff, updated for the Scala world. Each approach has advantages and disadvantages. With mixing traits, you will create fewer concrete objects, which can be easier to keep track of (as in above, you only have a single Module object, rather than separate objects for users and customers). On the other hand, traits must be mixed at object creation time, so you couldn't for example take an existing UserRepository and make a CustomerRepository by mixing it in -- if you need to do that, you must use aggregation. Note also that aggregation often requires you to specify singleton-types like above (: UserModule.type) in order for Scala to accept that the path-dependent types are the same. Another power that mixing traits has is that it can handle recursive dependencies -- both the UserModule and the CustomerModule can provide something to and require something from each other. This is also possible with aggregation using lazy vals, but it is more syntactically convenient with mixing traits.

这篇关于Scala Slick Cake Pattern:超过 9000 个类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆