【问题标题】:Jonas Bonér's dependency injection strategy seems limiting--but maybe I don't understand itJonas Bonér 的依赖注入策略似乎有限制——但也许我不明白
【发布时间】:2010-11-02 15:06:25
【问题描述】:

这篇文章我已经读了好几遍了:

http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di.html

我想我明白了。但是有些东西我不太明白。

查看他的 UserService 示例,我看到他设置了 UserRepositoryComponent 来封装 UserRepository。但是我不明白为什么 UserRepositoryComponent 扮演两个角色:它封装了 UserRepository 并且还提供了对 UserRepository 对象的引用。

我试图想象如果我想创建一个依赖于两个 UserRepository 实例的服务,我将如何使用这种模式。也许新服务的工作是将用户从“源”UserRepository 复制到“目标”UserRepository。所以我在想象这样的事情:

trait CopyUserServiceComponent {
  val source: UserRepositoryComponent
  val destination: UserRepositoryComponent
  class CopyUserServiceComponent { 
    ... 
  }
}

但这与原来的模式不同。在这种情况下,我在组件本身中定义依赖项,而不是从其他组件继承它们。但在我看来,这似乎是正确的做法:组件应该声明它们的依赖关系,而不是它们包含的服务的实例。

我在这里错过了什么?

【问题讨论】:

  • 实际上我认为“源”和“目标”应该是 UserRepository 类型,而不是 UserRepositoryComponent ......但为了让他们这样做,他们需要通过继承将 UserRepository 纳入范围以某种方式来自 UserRepositoryComponent...,然后 CopyUserServiceComponent 最终会得到一个从 UserRepositoryComponent 获得的无关 UserRepository。

标签: scala dependency-injection


【解决方案1】:

在这种情况下,我在组件本身中定义依赖项,而不是从其他组件继承它们。

蛋糕模式不使用继承来声明依赖关系。您在UserServiceComponent 中看到任何“扩展”吗?

但这在我看来是正确的做法:组件应该声明它们的依赖关系,而不是它们包含的服务的实例。

但这正是蛋糕模式所做的:声明依赖关系!如果示例包含def userRepositoryFactory = new UserRepository 而不是val userRepository = new UserRepository,或许会更清楚?

那么,让我们回到你的例子:

trait CopyUserServiceComponent {
  val source: UserRepositoryComponent
  val destination: UserRepositoryComponent
  class CopyUserServiceComponent { 
    ... 
  }
}

让我们看看我们不能用它做的事情:

trait CopyUserServiceComponent {
  // The module will need to see my internals!
  private val source: UserRepositoryComponent
  private val destination: UserRepositoryComponent
  class CopyUserServiceComponent { 
    ... 
  }
}

trait CopyBigUserServiceComponent extends CopyServiceComponent {
  // Any change in implementation will have to be reflected in the module!
  val tmp: UserRepositoryComponent
  ...
}

另一方面...

trait UserRepositoryComponent {
  val userRepositoryFactory: () => UserRepository

  class UserRepository {
    ...
  }
} 

trait CopyUserServiceComponent {
  self: UserRepositoryComponent =>
  // No problem here
  private val source: UserRepository = userRepositoryFactory()
  private val destination: UserRepository = userRepositoryFactory()
  class CopyUserServiceComponent { 
    ... 
  }
}

trait CopyBigUserServiceComponent extends CopyServiceComponent {
  self: UserRepositoryComponent =>
  // No problem here either
  val tmp: : UserRepository = userRepositoryFactory()
  ...
}

编辑

作为答案的补充,让我们考虑两种不同的需求:

  • 我需要UserRepository 的多个实例。

在这种情况下,您在错误的级别应用了模式。在 Jonas 的示例中,UserRepository 处于工厂提供单例的级别。

因此,在这种情况下,您不会使用UserRepositoryUserRepositoryComponent,而是使用UserRepositoryFactoryUserRepositoryFactoryComponent

  • 我正好需要两个单身UserRepository

在这种情况下,只需执行以下操作:

trait UserRepositoryComponent {
  val sourceUserService: UserService
  val destinationUserService: UserService

  class UserService ...
}

【讨论】:

  • 该服务使用继承来声明它的依赖关系,因为它要求“this”或“self”符合它自己以外的某个接口。服务本身不继承,但只能在具有继承权的其他组件中使用。
  • 无论如何,这个模式让我感到困惑的是(回到 Jonas 的例子)UserServiceComponent 声明了一个“val userService: UserService”。因此,通过从 UserServiceComponent 继承(参见前面的评论...),我得到了对 UserService 的依赖。但它在单个 UserService 上。如果我需要两个不同的,那怎么办?我不能继承两次。
  • 事后改变它是丑陋的,但如果认为你以后可能需要更多实例,你可以只使用 val userServices:List[UserService]。然而,从一个到多个 UserService 是一个很大的结构变化,如果不进行重构,这样的事情就不适用于其他策略。
  • 我认为这对我来说越来越清楚了。感谢您的所有解释。在需要两个 UserRepository 对象的情况下,认为我真正需要的是另一个表示两个 UserRepositories 组合的组件——一个 SourceDestUserRepositoryComponent 或类似的东西。
【解决方案2】:

我认为,Jonas 在他的文章中提到了一个被广泛接受的 methodology of building scalable applications,称为 Composite Software Construction,用几句话可以解释如下:整个应用程序(在元级别上编排)是一个由独立组件构建的程序集,它们又是其他组件和服务的组合。就复合软件而言,'cake'(示例中为ComponentRegistry 对象)是组件('UserServiceComponent' 和'UserRepositoryComponent')等的集合。而示例组件中包含服务实现它在现实世界中几乎不可能发生。

在您的示例中,您不需要定义内部类 - 您可以将您的工作流程放在一个普通的方法中:

trait CopyUserServiceComponent {
  val source: UserRepositoryComponent
  val destination: UserRepositoryComponent

  def copy = {...}
}

它完全符合原始模式 - 蛋糕的基本特征不仅是[仅]通过自类型注释指定依赖关系,而且还能够从具体实现中抽象出来,直到您需要从组件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-23
    • 2014-05-07
    • 1970-01-01
    • 2012-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多