【问题标题】:Scala Dependency Injection with Cake Pattern使用 Cake 模式的 Scala 依赖注入
【发布时间】:2016-11-21 04:56:11
【问题描述】:

我一直在关注这篇文章,它描述了如何通过 Cake Pattern 在 Scala 中实现依赖注入: http://jonasboner.com/real-world-scala-dependency-injection-di/

我对 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)

我不明白的是,我现在如何将假存储库传递给 Account Service,比如将输出更改为“Test”而不是“Hello”?

【问题讨论】:

    标签: scala dependency-injection


    【解决方案1】:

    可以通过多种方式对其进行修改以实现可行的结果,具体取决于对您的情况而言什么是可行的结果。我将在这里介绍一个更简单的可能性。

    首先,ComponentRegistry 需要成为一个 trait,所以它可以混入 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)
    

    这应该像以前一样打印“Hello”。要设置测试用例,请添加以下内容:

    // 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
    }
    

    现在我们可以设置一个使用测试组件的服务:

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

    这应该打印“测试”。

    请注意,您可能希望您的 AccountService 根据其他 mixins/traits 来定义其功能,这会期望适当的组件可用(分层到“蛋糕”中),但不知道哪个实现在利用。例如:

    trait CustomerApi {
      self: AccountRepositoryComponent => // Expects an implementation of AccountRepositoryComponent to be mixed in
    
      def say: String = accountRepository.message
    }
    

    现在方法say 的实现并不知道它将与哪个版本的AccountRepository 交互,但知道必须提供一个(在编译时检查)。所以我们可以这样写:

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

    调用println(AccountService.say) 将生成"Hello",而调用println(TestAccountService.say) 将生成"Test"

    【讨论】:

    • 谢谢,所以它比实际注射更多?基于此,模拟 AccountRepository 进行测试的最佳方法是什么? (假设 specs2/Mockito)
    • 知道了,我试图模拟一个对象,如果我模拟“接口”而不是它工作正常。
    【解决方案2】:

    This post 提供了一个简洁的例子(后面是一个有趣的替代方案)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-25
      • 2015-05-10
      • 2012-12-29
      • 2017-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多