在 scala 中,什么是更简洁的代码 - def (~member) 与传递函数参数?

in scala what is cleaner code - to def (~member) versus pass function parameter?

哪个更干净?

默认版本

trait Foo {
  def db: DB
  def save() = db.save()
  def load() = db.load()
}

对比参数化版本

trait Foo {
  def save(db: DB) = db.save()
  def load(db: DB) = db.load()
}

我不得不说,当我看到 复杂的 项目时,我感谢上帝,因为函数在 [=13] 中占用了它们所有的 依赖项=]

  1. 我可以轻松地单元测试它们而无需覆盖成员,这些函数告诉我它依赖于其签名的所有内容。
  2. 我不需要阅读他们的内部代码来更好地理解函数的作用,我有它的名称,我有它的输入,我的输出都在函数signature.

但我也注意到在 scala 中它非常传统 使用 def 版本,我不得不说这段代码捆绑在复杂的项目中这样的代码对我来说可读性要差得多。我错过了什么吗?

我认为在这种情况下,它在很大程度上取决于 FooDB 之间的关系。 Foo 的单个实例是否会使用一个 DB 用于 load 而另一个用于 save?如果是,那么 DB 并不是 Foo 的真正依赖项,第一个示例没有意义。但在我看来答案是否定的,如果你用一个 DB 调用 load,你将在调用 save 时使用相同的 DB

在您的第一个示例中,该信息被编码到类型系统中。您实际上是在让编译器为您做一些正确性检查,因为现在您在编译时强制执行单个 Fooloadsave 将在相同的 DB(是的,db 可能是 var,但这本身就是另一个问题)。

此外,您似乎不可避免地会在每个经过 Foo 的地方绕过 DB。假设您有一个使用 Foo 的函数。在第一个示例中,您的函数看起来像

def loadFoo(foo: Foo) {
  foo.load()
}

而在第二个中它看起来像:

def loadFoo(foo: Foo, db: DB) {
  foo.load(db)
}

所以您所做的只是加长了每个函数签名并为错误留出了空间。

最后,我认为您关于单元测试和不需要阅读函数代码的观点是无效的。在第一个示例中,确实无法仅通过查看函数签名来查看 load 的所有依赖项。但是 load 不是一个孤立的函数,它是一个作为特征一部分的方法。方法与普通的旧函数不同,它们存在于定义特征的上下文中。

换句话说,您不应该考虑对功能进行单元测试,而应该对特征进行单元测试。他们是一揽子交易,你不应该期望他们的行为是相互独立的。如果你确实想要那种独立性,那么 Foo 应该是一个 object ,它基本上使 loadsave 静态方法(尽管即使那样对象也可以有内部状态,但是那远不那么惯用)。

此外,您永远无法真正仅通过查看函数的依赖关系来判断函数在做什么。毕竟我可以写一个函数:

def save(db: DB){
  throw new Exception("hello!!")
}