JonasBonér的依赖注入策略似乎有限 - 但也许我不明白

时间:2010-11-02 15:06:25

标签: scala dependency-injection

我已多次阅读这篇文章:

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 { 
    ... 
  }
}

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

我在这里缺少什么?

2 个答案:

答案 0 :(得分:4)

  

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

蛋糕模式不使用继承来声明依赖项。你在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
  • 的许多实例

在这种情况下,您将以错误的级别应用模式。在乔纳斯的例子中,UserRepository处于工厂提供的单身人士的水平。

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

  • 我需要两个单身UserRepository

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

trait UserRepositoryComponent {
  val sourceUserService: UserService
  val destinationUserService: UserService

  class UserService ...
}

答案 1 :(得分:1)

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

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

trait CopyUserServiceComponent {
  val source: UserRepositoryComponent
  val destination: UserRepositoryComponent

  def copy = {...}
}

它完全符合原始模式 - 蛋糕的基本特征不是[仅]通过自我类型注释指定依赖关系,而是从具体实现中抽象出来的能力,直到你需要从中构建一个程序集的那一刻。组件。