我已多次阅读这篇文章:
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 {
...
}
}
但这与原始模式不同。在这种情况下,我在组件本身中定义依赖项,而不是从其他组件继承它们。但在我看来,这是正确的方法:组件应该声明它们的依赖关系,而不是它们包含的服务的实例。
我在这里缺少什么?
答案 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
处于工厂提供的单身人士的水平。
因此,在这种情况下,您不会UserRepository
和UserRepositoryComponent
,而是UserRepositoryFactory
和UserRepositoryFactoryComponent
。
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 = {...}
}
它完全符合原始模式 - 蛋糕的基本特征不是[仅]通过自我类型注释指定依赖关系,而是从具体实现中抽象出来的能力,直到你需要从中构建一个程序集的那一刻。组件。