Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala隐式类型类依赖项注入_Scala_Dependency Injection_Akka_Typeclass - Fatal编程技术网

Scala隐式类型类依赖项注入

Scala隐式类型类依赖项注入,scala,dependency-injection,akka,typeclass,Scala,Dependency Injection,Akka,Typeclass,我需要一些帮助来解决这个问题。我有一个Akka actor,我想在其中注入一个依赖项,在本例中是RemoteFetcher,我也希望在测试中模拟它。像这样: main/src/scala/mypackage/Services.scala package mypackage import RemoteFetcherFileSystem._ trait RemoteFetcher { def fetch( path:String ): Future[Stream[String]] } cla

我需要一些帮助来解决这个问题。我有一个Akka actor,我想在其中注入一个依赖项,在本例中是RemoteFetcher,我也希望在测试中模拟它。像这样:

main/src/scala/mypackage/Services.scala

package mypackage
import RemoteFetcherFileSystem._

trait RemoteFetcher {
  def fetch( path:String ): Future[Stream[String]]
}

class MyRemoteResourceActor extends Actor with ActorLogging {
  def fetchRemote( path:String ) = implicitly[RemoteFetcher].fetch( path )
  def receive = {
     case FetchRemoteResource( path ) => fetchRemote( path ).map( _.foreach( sender ! _ ) )
  }
}
为了实现这一点,我有一个隐式对象,我将其导入到上面的文件中。看起来像这样:

implicit object RemoteFetcherFileSystem extends RemoteFetcher {
   def fetchRemote( path:String ) = Future[Stream[String]] { ... reading from file system ... }
}
现在在我的测试中,我有来自akka测试工具包的TestActor。在这里,我想导入我的模拟依赖项:

implicit object RemoteFetcherMock extends RemoteFetcher {
   def fetchRemote( path:String ) = Future[Stream[String]] { ... mock implementation ... }
}
我的问题是,要编译Services.scala,我需要导入隐式对象。但是如何在测试文件中对其进行阴影/覆盖呢。我不使用隐式参数的原因是我想避免修改所有的actors构造函数参数


当我环顾四周并阅读类型类依赖项注入模式时,我发现它可以根据教程工作,但当我想测试和重写时,我无法让它工作,就像在我的示例中一样。

我不确定如何使用隐式注入,但通常可以这样注入:

trait RemoteFetcherComponent {
  def remoteFetcher: RemoteFetcher
  trait RemoteFetcher {
    def fetch(path: String): Future[Stream[String]]
  }
}

trait RemoteFetcherFileSystemComponent extends RemoteFetcherComponent {
   val remoteFetcher = RemoteFetcherFileSystem
   object RemoteFetcherFileSystem extends RemoteFetcher {
     def fetch(path: String): Future[Stream[String]] = ???
   }
}

class MyRemoteResourceActor extends Actor with ActorLogging with RemoteFetcherFileSystemComponent {
  def fetchRemote(path: String) = remoteFetcher.fetch(path)
  def receive = {
     case FetchRemoteResource(path) => fetchRemote(path).map( _.foreach(sender ! _))
  }
}

val myRemoteResourceActor = new MyRemoteResourceActor()
trait RemoteFetcherMockComponent extends RemoteFetcherComponent {
  def remoteFetcher = RemoteFetcherMock
  object RemoteFetcherMock extends RemoteFetcher {
    def fetch(path: String): Future[Stream[String]] = ???
  }
}

val myMockedResourceActor = new MyRemoteResourceActor with RemoteFetcherMockComponent {
  override val remoteFetcher = super[RemoteFetcherMockComponent].remoteFetcher
}
然后测试值的定义如下:

trait RemoteFetcherComponent {
  def remoteFetcher: RemoteFetcher
  trait RemoteFetcher {
    def fetch(path: String): Future[Stream[String]]
  }
}

trait RemoteFetcherFileSystemComponent extends RemoteFetcherComponent {
   val remoteFetcher = RemoteFetcherFileSystem
   object RemoteFetcherFileSystem extends RemoteFetcher {
     def fetch(path: String): Future[Stream[String]] = ???
   }
}

class MyRemoteResourceActor extends Actor with ActorLogging with RemoteFetcherFileSystemComponent {
  def fetchRemote(path: String) = remoteFetcher.fetch(path)
  def receive = {
     case FetchRemoteResource(path) => fetchRemote(path).map( _.foreach(sender ! _))
  }
}

val myRemoteResourceActor = new MyRemoteResourceActor()
trait RemoteFetcherMockComponent extends RemoteFetcherComponent {
  def remoteFetcher = RemoteFetcherMock
  object RemoteFetcherMock extends RemoteFetcher {
    def fetch(path: String): Future[Stream[String]] = ???
  }
}

val myMockedResourceActor = new MyRemoteResourceActor with RemoteFetcherMockComponent {
  override val remoteFetcher = super[RemoteFetcherMockComponent].remoteFetcher
}

您对implicits有问题的原因是,您使用它的方式与简单地使用
defetchremote(path:String)=RemoteFetcherFileSystem.fetch(path)
没有什么不同。通过导入,您已经定义了实现,而不是允许稍后将其注入。

您还可以将
隐式更改为隐式参数:

trait RemoteFetcher {
  def fetch(path: String): Future[Stream[String]]
}

object RemoteFetcher {
   implicit val fetcher = RemoteFetcherFileSystem
}

class MyRemoteResourceActor extends Actor with ActorLogging {
  def fetchRemote(path: String)(implicit remoteFetcher: RemoteFetcher) = remoteFetcher.fetch(path)
  def receive = {
     case FetchRemoteResource(path) => fetchRemote(path).map( _.foreach(sender ! _))
  }
}
然后,只需导入
RemoteFetcherMock
,就可以覆盖在
RemoteFetcher
的伴随对象中解析的隐式


有关隐式参数解析优先规则的详细信息,请参见

所以我猜你是建议在这里使用蛋糕图案?嗯,值得一试。在我阅读的教程中,我得到的印象是它非常冗长,但看起来并不太糟糕。我添加了另一个答案,其中有一种使用隐式的方法。也许不是你想要的方式,也不是最好的方式,但这绝对是一个起点。我想你是想让演员扩展RemoteFetcherFileSystemComponent?不,只是trait
RemoteFetcherComponent
。然后,当您实例化它时,您将混合
RemoteFetcherFileSystemComponent
,请参阅我的示例中的
val
。Hmmm。。。不确定这是否适用于我的用例(至少正如我在上面定义的那样)。因为我自己并没有把这个演员当例子。然后我可以再次传递构造函数参数。我明天还要再看一看,我想我还是把参数传递给构造函数为好。我试过了。但由于某种原因,它并没有真正为我工作,因为我得到了隐式歧义冲突(可能是REPL在捉弄我)。我想这也是一种选择。我想我最后还是会选择蛋糕的式样。