为什么使用 Scala 的蛋糕模式而不是抽象字段? [英] Why use scala's cake pattern rather than abstract fields?

查看:21
本文介绍了为什么使用 Scala 的蛋糕模式而不是抽象字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在通过 蛋糕图案.我想我明白了,但我一定错过了一些东西,因为我仍然看不到其中的意义!为什么通过 self 类型而不只是抽象字段来声明依赖项更可取?

I have been reading about doing Dependency Injection in scala via the cake pattern. I think I understand it but I must have missed something because I still can't see the point in it! Why is it preferable to declare dependencies via self types rather than just abstract fields?

编程 Scala TwitterClientComponent 中的示例为例代码>使用蛋糕模式声明这样的依赖关系:

Given the example in Programming Scala TwitterClientComponent declares dependencies like this using the cake pattern:

//other trait declarations elided for clarity
...

trait TwitterClientComponent {

  self: TwitterClientUIComponent with
        TwitterLocalCacheComponent with
        TwitterServiceComponent =>

  val client: TwitterClient

  class TwitterClient(val user: TwitterUserProfile) extends Tweeter {
    def tweet(msg: String) = {
      val twt = new Tweet(user, msg, new Date)
      if (service.sendTweet(twt)) {
        localCache.saveTweet(twt)
        ui.showTweet(twt)
      }
    }
  }
}

这比将依赖项声明为抽象字段(如下所示)有什么好处?

How is this better than declaring dependencies as abstract fields as below?

trait TwitterClient(val user: TwitterUserProfile) extends Tweeter {
  //abstract fields instead of cake pattern self types
  val service: TwitterService
  val localCache: TwitterLocalCache
  val ui: TwitterClientUI

  def tweet(msg: String) = {
    val twt = new Tweet(user, msg, new Date)
    if (service.sendTweet(twt)) {
      localCache.saveTweet(twt)
      ui.showTweet(twt)
    }
  }
}

查看实例化时间,也就是 DI 实际发生的时间(据我所知),我很难看到 cake 的优势,尤其是当您考虑为 cake 声明(封闭 trait)

Looking at instantiation time, which is when DI actually happens (as I understand it), I am struggling to see the advantages of cake, especially when you consider the extra keyboard typing you need to do for the cake declarations (enclosing trait)

    //Please note, I have stripped out some implementation details from the 
    //referenced example to clarify the injection of implemented dependencies

    //Cake dependencies injected:
    trait TextClient
        extends TwitterClientComponent
        with TwitterClientUIComponent
        with TwitterLocalCacheComponent
        with TwitterServiceComponent {


      // Dependency from TwitterClientComponent:
      val client = new TwitterClient

      // Dependency from TwitterClientUIComponent:
      val ui = new TwitterClientUI

      // Dependency from TwitterLocalCacheComponent:
      val localCache = new TwitterLocalCache 

      // Dependency from TwitterServiceComponent
      val service = new TwitterService
    }

现在再次使用抽象字段,或多或少相同!:

Now again with abstract fields, more or less the same!:

trait TextClient {
          //first of all no need to mixin the components

          // Dependency on TwitterClient:
          val client = new TwitterClient

          // Dependency on TwitterClientUI:
          val ui = new TwitterClientUI

          // Dependency on TwitterLocalCache:
          val localCache = new TwitterLocalCache 

          // Dependency on TwitterService
          val service = new TwitterService
        }

我确定我一定错过了蛋糕的优越性!但是,目前我看不到它通过以任何其他方式(构造函数、抽象字段)声明依赖项提供了什么.

I'm sure I must be missing something about cake's superiority! However, at the moment I can't see what it offers over declaring dependencies in any other way (constructor, abstract fields).

推荐答案

具有自类型注释的特征比具有字段注入的老式 bean 更具组合性,您可能在第二个代码段中想到了这一点.

Traits with self-type annotation is far more composable than old-fasioned beans with field injection, which you probably had in mind in your second snippet.

让我们看看你将如何实例化这个特性:

Let's look how you will instansiate this trait:

val productionTwitter = new TwitterClientComponent with TwitterUI with FSTwitterCache with TwitterConnection

如果你需要测试这个特性,你可能会这样写:

If you need to test this trait you probably write:

val testTwitter = new TwitterClientComponent with TwitterUI with FSTwitterCache with MockConnection

嗯,有点 DRY 违规.让我们改进.

Hmm, a little DRY violation. Let's improve.

trait TwitterSetup extends TwitterClientComponent with TwitterUI with FSTwitterCache
val productionTwitter = new TwitterSetup with TwitterConnection
val testTwitter = new TwitterSetup with MockConnection

此外,如果您的组件中的服务之间存在依赖关系(比如 UI 依赖于 TwitterService),它们将被编译器自动解析.

Furthermore if you have a dependency between services in your component (say UI depends on TwitterService) they will be resolved automatically by the compiler.

这篇关于为什么使用 Scala 的蛋糕模式而不是抽象字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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