简体   繁体   English

Scala依赖注入与蛋糕模式

[英]Scala Dependency Injection with Cake Pattern

I've been following this article which describes how to achieve dependency injection in Scala via the Cake Pattern: http://jonasboner.com/real-world-scala-dependency-injection-di/ 我一直在关注这篇文章,它描述了如何通过Cake Pattern在Scala中实现依赖注入: http//jonasboner.com/real-world-scala-dependency-injection-di/

I'm kind of new to Scala and I admit some of it went over my head, so far I've got the following working: 我对Scala有点新意,我承认其中有些事情已经过去了,到目前为止,我已经完成了以下工作:

// Setup the component and interface
trait AccountRepositoryComponent {
  val accountRepository: AccountRepositoryInterface

  trait AccountRepositoryInterface {
    def message: String
  }
}

// An implementation
trait MyAccountRepositoryComponent extends AccountRepositoryComponent {
  object AccountRepository extends AccountRepositoryInterface {
    def message: String = "Hello"
  }
}

// Object to configure which implementations to use and retrieve them
object ComponentRegistry extends MyAccountRepositoryComponent {
  val accountRepository = AccountRepository
}

// Example service using the above
object AccountService { 
  val repo = ComponentRegistry.accountRepository
  def say: String = repo.message
}

println(AccountService.say)

What I'm failing to understand is how I would now pass in a fake repository to Account Service, say to change the output to "Test" rather than "Hello"? 我无法理解的是我现在如何将假存储库传递给帐户服务,比如将输出更改为“测试”而不是“Hello”?

There are various ways this could be modified to achieve a workable result, depending on what counts as a workable result for your situation. 可以通过多种方式对其进行修改以实现可行的结果,具体取决于您的情况可行的结果。 I'll go through a simpler possibility here. 我会在这里找到一个更简单的可能性。

First, the ComponentRegistry needs to become a trait, so it can be mixed in to the AccountService: 首先,ComponentRegistry需要成为一个特征,因此它可以混合到AccountService中:

// Trait to configure which component implementations to use and retrieve them
object ComponentRegistry extends MyAccountRepositoryComponent {
  val accountRepository = AccountRepository
}

// Example service using the above
object AccountService extends ComponentRegistry { 
  def say: String = accountRepository.message
}

println(AccountService.say)

This should print "Hello" as before. 这应该像以前一样打印“你好”。 To set up a test case, add the following: 要设置测试用例,请添加以下内容:

// Test implementation
trait TestAccountRepositoryComponent extends AccountRepositoryComponent {
  object AccountRepository extends AccountRepositoryInterface {
    def message: String = "Test"
  }
}

// trait to configure test component implementations
trait TestComponentRegistry extends TestAccountRepositoryComponent {
  val accountRepository = AccountRepository
}

Now we can set up a service that uses the test components: 现在我们可以设置一个使用测试组件的服务:

// Example service using the above
object AccountService extends TestComponentRegistry { 
  //val repo = ComponentRegistry.accountRepository
  def say: String = accountRepository.message
}

println(AccountService.say)

This should print "Test". 这应该打印“测试”。

Note that you would probably want your AccountService to define its functionality in terms of other mixins/traits, which would expect the appropriate components to be available (layered into the "cake"), but wouldn't know which implementation was in use. 请注意,您可能希望您的AccountService根据其他mixins / traits定义其功能,这将期望适当的组件可用(分层到“蛋糕”),但不知道正在使用哪个实现。 Eg: 例如:

trait CustomerApi {
  self: AccountRepositoryComponent => // Expects an implementation of AccountRepositoryComponent to be mixed in

  def say: String = accountRepository.message
}

Now the method say is implemented without knowing what version of AccountRepository it will interact with, but knowing one must be provided (checked at compile time). 现在say方法是在不知道它将与哪个版本的AccountRepository交互的情况下实现的,但必须提供一个知道版本(在编译时检查)。 So we can write: 所以我们可以写:

object AccountService extends CustomerApi with ComponentRegistry
object TestAccountService extends CustomerApi with TestComponentRegistry

Calling println(AccountService.say) will generate "Hello" , while calling println(TestAccountService.say) will generate "Test" . 调用println(AccountService.say)将生成"Hello" ,而调用println(TestAccountService.say)将生成"Test"

这篇文章提供了一个简洁的例子(后面是一个有趣的替代方案)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM