Scala蛋糕模式和依赖冲突 [英] Scala Cake Pattern and Dependency Collisions

查看:112
本文介绍了Scala蛋糕模式和依赖冲突的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Cake Pattern在Scala中实现依赖注入,但是遇到了依赖冲突.由于我找不到具有此类依赖关系的详细示例,因此这是我的问题:

I'm trying to implement dependency injection in Scala with the Cake Pattern, but am running into dependency collisions. Since I could not find a detailed example with such dependencies, here's my problem:

假设我们具有以下特征(具有2种实现):

Suppose we have the following trait (with 2 implementations):

trait HttpClient {
  def get(url: String)
}

class DefaultHttpClient1 extends HttpClient {
  def get(url: String) = ???
}

class DefaultHttpClient2 extends HttpClient {
  def get(url: String) = ???
}

以及以下两个蛋糕模式模块(在此示例中,这两个API均依赖于我们的HttpClient来实现其功能):

And the following two cake pattern modules (which in this example are both APIs that depend on our HttpClient for their functionality):

trait FooApiModule {
  def httpClient: HttpClient        // dependency
  lazy val fooApi = new FooApi()    // providing the module's service

  class FooApi {
    def foo(url: String): String = {
      val res = httpClient.get(url)
      // ... something foo specific
      ???
    }
  }
}

trait BarApiModule {
  def httpClient: HttpClient        // dependency
  lazy val barApi = new BarApi()    // providing the module's service

  class BarApi {
    def bar(url: String): String = {
      val res = httpClient.get(url)
      // ... something bar specific
      ???
    }
  }
}

现在,当创建使用两个模块的最终应用程序时,我们需要为两个模块提供httpClient依赖关系.但是,如果我们想为每个模块提供不同的实现,该怎么办?还是只是简单地提供不同配置的依赖项实例(例如,使用不同的ExecutionContext?)?

Now when creating the final app that uses both modules, we need to provide the httpClient dependency for both of the modules. But what if we want to provide a different implementation of it for each of the modules? Or simply provide different instances of the dependency configured differently (say with a different ExecutionContext for example)?

object MyApp extends FooApiModule with BarApiModule {
  // the same dependency supplied to both modules
  val httpClient = new DefaultHttpClient1()

  def run() = {
    val r1 = fooApi.foo("http://...")
    val r2 = barApi.bar("http://...")
    // ...
  }
}

我们可以在每个模块中以不同的方式命名依赖关系,为它们加上模块名称前缀,但这将是繁琐且笨拙的,而且如果我们自己无法完全控制这些模块,也将无法正常工作.

We could name the dependencies differently in each module, prefixing them with the module name, but that would be cumbersome and inelegant, and also won't work if we don't have full control of the modules ourselves.

有什么想法吗?我误解了蛋糕图案吗?

Any ideas? Am I misinterpreting the Cake Pattern?

推荐答案

您正确地获得了模式,并且刚刚发现了它的重要局限性.如果两个模块依赖于某个对象(例如HttpClient),并且碰巧使用相同的名称(例如httpClient)来声明它,那么游戏就结束了-您不会在一个Cake中单独配置它们.可以像Daniel所建议的那样拥有两个蛋糕,或者,如果可以的话,请更改模块的来源(如Tomer Gabel所暗示的那样).

You get the pattern correctly and you've just discovered its important limitation. If two modules depend on some object (say HttpClient) and happen to declare it under the same name (like httpClient), the game is over - you won't configure them separately inside one Cake. Either have two Cakes, like Daniel advises or change modules' sources if you can (as Tomer Gabel is hinting).

每种解决方案都有其问题.

Each of those solutions has its problems.

拥有两个蛋糕(丹尼尔的建议)看起来很好,只要它们不需要一些公共依赖项即可.

Having two Cakes (Daniel's advice) looks well as long they don't need some common dependencies.

重命名某些依赖项(如果可能的话)会迫使您调整所有使用依赖项的代码.

Renaming some dependencies (provided it's possible) forces you to adjust all code that uses those.

因此,有些人(包括我在内)更喜欢不受这些问题影响的解决方案,例如使用普通的旧构造函数,而完全避免使用Cake.如果您进行了测量,它们不会给代码增加太多膨胀(Cake已经非常冗长),并且它们更加灵活.

Therefore some people (including me) prefer solutions immune to those problems, like using plain old constructors and avoid Cake altogether. If you measured it, they don't add much bloat to the code (Cake is already pretty verbose) and they're much more flexible.

这篇关于Scala蛋糕模式和依赖冲突的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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