在Scala中使用蛋糕模式和函数之间的区别 - 为什么蛋糕模式有用?

9 浏览
0 Comments

在Scala中使用蛋糕模式和函数之间的区别 - 为什么蛋糕模式有用?

我在思考在Scala中使用函数和Cake模式进行依赖注入的区别。我得出了以下理解,想知道这个理解是否正确。

让我们想象一个依赖图。

1) 如果我们使用函数作为构建块,那么图中的节点将是函数,边将是参数。

2) 如果我们使用特质作为构建块(就像Cake模式中那样),那么图中的节点将是特质,边将是抽象成员。

那么Cake模式的目的是什么?为什么2比1更好?它是为了粗粒度的组织。图1可以通过将函数分组到特质中来简化,这样我们就得到了一个更小、更易理解的图2。相关概念的分组/聚类是一种压缩形式,并创建了理解(我们需要记住的东西更少,就能理解)。

这里是另一个比较(Cake模式与包系统之间的比较):

Cake类似于将相关函数分组到包中,但它超越了这一点,因为使用命名空间(包/对象)会导致依赖关系被硬编码,而Cake则用特质和自类型注释/抽象成员替代了包/对象和导入(import)。包和Cake模式之间的区别在于,在使用Cake时,一个依赖的实际实现可以改变,而在使用包时是无法改变的。

我不知道这些类比是否有意义,如果不对,请纠正我,如果对,请向我保证。我仍然在努力理解Cake模式以及如何与我已经理解的概念(函数、包)相关联。

0
0 Comments

在Scala中,依赖注入(DI)通常使用获取器/设置器(即函数)和/或构造函数参数来实现。获取器/设置器的方法可能如下所示:

trait Logger { 
  // fancy logging stuff 
}
class NeedsALogger {
  private var l: Logger = _
  def logger: Logger = l
  def logger_=(newLogger: Logger) {
    l = newLogger
  }
  // uses a Logger here
}

但是我不太喜欢使用获取器/设置器的方法。它无法保证依赖项是否被注入。如果使用某些DI框架,可以强制要求注入某些内容,但这样一来,DI就不再与框架无关了。如果使用构造函数的方法,那么无论使用哪个框架,都必须在实例化时提供依赖项:

class NeedsALogger(logger: Logger) {

// uses a Logger here

}

那么,Cake Pattern又是如何适应这个问题的呢?首先,让我们将我们的示例改写为Cake Pattern的形式:

class NeedsALogger {
  logger: Logger => 
  // Uses a Logger here
}

让我们来讨论一下`logger: Logger =>`。这是一个self-type,它简单地将`Logger`的成员引入作用域,而无需扩展`Logger`。`NeedsALogger`不是一个`Logger`,所以我们不想扩展它。然而,`NeedsALogger`需要一个`Logger`,这就是我们通过self-type来实现的。我们要求在创建`NeedsALogger`时必须提供一个`Logger`。使用方法如下:

trait FooLogger extends Logger {
  // full implementation of Logger
}
trait BarLogger extends Logger {
  // full implementation of Logger
}
val a = new NeedsALogger with FooLogger
val b = new NeedsALogger with BarLogger
val c = new NeedsALogger // compile-time error! 

正如你所看到的,我们可以使用任何一种方法来实现相同的效果。对于大多数DI来说,构造函数的方法就足够了,所以你可以根据自己的喜好来选择。个人而言,我喜欢self-type和Cake Pattern,但我也看到很多人避免使用它。

要继续了解Cake Pattern的具体内容,请查看这里。这是一个很好的下一步,如果你想了解更多信息的话。

0