如何在不进行硬编码的情况下使用 Cake 模式进行依赖注入?

发布于 2024-10-20 03:40:21 字数 211 浏览 0 评论 0原文

我刚刚阅读并喜欢蛋糕模式文章。然而,在我看来,使用依赖注入的关键原因之一是您可以改变 XML 文件或命令行参数所使用的组件。

Cake 模式如何处理 DI 的这一方面?我见过的例子都涉及静态混合特征。

I just read and enjoyed the Cake pattern article. However, to my mind, one of the key reasons to use dependency injection is that you can vary the components being used by either an XML file or command-line arguments.

How is that aspect of DI handled with the Cake pattern? The examples I've seen all involve mixing traits in statically.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

转角预定愛 2024-10-27 03:40:21

由于特征的混合在 Scala 中是静态完成的,因此如果您想要改变混合到对象中的特征,请根据某些条件创建不同的对象。

让我们以一个典型的蛋糕图案为例。您的模块被定义为特征,并且您的应用程序被构造为一个简单的对象,其中混合了一堆功能

val application =
    new Object
extends Communications
   with Parsing
   with Persistence
   with Logging
   with ProductionDataSource
application.startup

现在所有这些模块都有很好的自类型声明,这些声明定义了它们的模块间依赖关系,因此只有当您的所有模块都被编译时,该行才会编译模块间依赖关系存在、唯一且类型良好。特别是,Persistence 模块有一个 self 类型,它表示任何实现 Persistence 的东西都必须实现 DataSource,这是一个抽象模块特征。由于 ProductionDataSource 继承自 DataSource,所以一切都很好,并且该应用程序构建线可以编译。

但是,如果您想使用不同的数据源,指向某个本地数据库用于测试目的,该怎么办?进一步假设您不能仅使用从某些属性文件加载的不同配置参数来重用 ProductionDataSource。在这种情况下,您要做的就是定义一个扩展 DataSource 的新特征 TestDataSource,并将其混合进去。您甚至可以根据命令行标志动态地执行此操作。

val application = if (test)
  new Object
    extends Communications
      with Parsing
      with Persistence
      with Logging
      with TestDataSource
else
  new Object
    extends Communications
      with Parsing
      with Persistence
      with Logging
      with ProductionDataSource

application.startup

现在看起来比我们想要的更冗长,特别是如果您的应用程序需要在多个轴上改变其结构。从好的方面来说,您通常只有一块条件构造逻辑,就像应用程序中的那样(或者最坏的情况是每个可识别组件生命周期一次),因此至少可以最大程度地减少痛苦并与其余逻辑隔离开来。

Since mixing in traits is done statically in Scala, if you want to vary the traits mixed in to an object, create different objects based on some condition.

Let's take a canonical cake pattern example. Your modules are defined as traits, and your application is constructed as a simple Object with a bunch of functionality mixed in

val application =
    new Object
extends Communications
   with Parsing
   with Persistence
   with Logging
   with ProductionDataSource
application.startup

Now all of those modules have nice self-type declarations which define their inter-module dependencies, so that line only compiles if your all inter-module dependencies exist, are unique, and well-typed. In particular, the Persistence module has a self-type which says that anything implementing Persistence must also implement DataSource, an abstract module trait. Since ProductionDataSource inherits from DataSource, everything's great, and that application construction line compiles.

But what if you want to use a different DataSource, pointing at some local database for testing purposes? Assume further that you can't just reuse ProductionDataSource with different configuration parameters, loaded from some properties file. What you would do in that case is define a new trait TestDataSource which extends DataSource, and mix it in instead. You could even do so dynamically based on a command line flag.

val application = if (test)
  new Object
    extends Communications
      with Parsing
      with Persistence
      with Logging
      with TestDataSource
else
  new Object
    extends Communications
      with Parsing
      with Persistence
      with Logging
      with ProductionDataSource

application.startup

Now that looks a bit more verbose than we would like, particularly if your application needs to vary its construction on multiple axes. On the plus side, you usually you only have one chunk of conditional construction logic like that in an application (or at worst once per identifiable component lifecycle), so at least the pain is minimized and fenced off from the rest of your logic.

脱离于你 2024-10-27 03:40:21

Scala 也是一种脚本语言。因此您的配置 XML 可以是 Scala 脚本。它是类型安全的并且不是不同的语言。

简单地看一下启动:

scala -cp first.jar:second.jar startupScript.scala

与:

java -cp first.jar:second.jar com.example.MyMainClass context.xml

您始终可以使用 DI,但您多了一个工具。

Scala is also a script language. So your configuration XML can be a Scala script. It is type-safe and not-a-different-language.

Simply look at startup:

scala -cp first.jar:second.jar startupScript.scala

is not so different than:

java -cp first.jar:second.jar com.example.MyMainClass context.xml

You can always use DI, but you have one more tool.

抠脚大汉 2024-10-27 03:40:21

简而言之,Scala 目前没有任何对动态 mixin 的内置支持。

我正在开发 autoproxy-plugin 来支持这一点,尽管它目前处于搁置状态,直到 2.9 版本发布,届时编译器将具有新功能,使其成为一项更容易的任务。

同时,实现几乎完全相同的功能的最佳方法是将动态添加的行为实现为包装类,然后将隐式转换添加回包装的成员。

The short answer is that Scala doesn't currently have any built-in support for dynamic mixins.

I am working on the autoproxy-plugin to support this, although it's currently on hold until the 2.9 release, when the compiler will have new features making it a much easier task.

In the meantime, the best way to achieve almost exactly the same functionality is by implementing your dynamically added behavior as a wrapper class, then adding an implicit conversion back to the wrapped member.

陌路终见情 2024-10-27 03:40:21

在 AutoProxy 插件可用之前,实现该效果的一种方法是使用委托:

trait Module {
  def foo: Int
}

trait DelegatedModule extends Module {
  var delegate: Module = _
  def foo = delegate.foo
}

class Impl extends Module {
  def foo = 1
}

// later
val composed: Module with ... with ... = new DelegatedModule with ... with ...
composed.delegate = choose() // choose is linear in the number of `Module` implementations

但要注意,这样做的缺点是比较冗长,如果使用 var,则必须小心初始化顺序code> 位于特征内。另一个缺点是,如果上面的 Module 中有路径依赖类型,您将无法轻松使用委托。

但是,如果存在大量可以变化的不同实现,那么与列出具有所有可能组合的案例相比,它可能会花费更少的代码。

Until the AutoProxy plugin becomes available, one way to achieve the effect is to use delegation:

trait Module {
  def foo: Int
}

trait DelegatedModule extends Module {
  var delegate: Module = _
  def foo = delegate.foo
}

class Impl extends Module {
  def foo = 1
}

// later
val composed: Module with ... with ... = new DelegatedModule with ... with ...
composed.delegate = choose() // choose is linear in the number of `Module` implementations

But beware, the downside of this is that it's more verbose, and you have to be careful about the initialization order if you use vars inside a trait. Another downside is that if there are path dependent types within Module above, you won't be able to use delegation that easily.

But if there is a large number of different implementations that can be varied, it will probably cost you less code than listing cases with all possible combinations.

恋你朝朝暮暮 2024-10-27 03:40:21

Lift 内置了类似的东西。它主要是用 scala 代码编写的,但你有一些运行时控制。 http://www.assembla.com/wiki/show/liftweb/Dependency_Injection

Lift has something along those lines built in. It's mostly in scala code, but you have some runtime control. http://www.assembla.com/wiki/show/liftweb/Dependency_Injection

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文