0

我的问题与Scala Cake Pattern 和 Dependency Collisions非常相似。但我正在努力寻找 Daniel C 的回答中建议的具体解决方案。

所以这里是问题:

A ProductDetailsPage(trait) 需要两个独立的服务模块ProductServiceModuleSessionModule,分别由ProductServiceModuleWs和实现SessionModuleWs

这两个模块都依赖于RestServiceConfigurationProvider.

为此RestServiceConfigurationProvider,只有一个具体的实现可用:DefaultRestServiceConfigurationProvider(atm)。

DefaultRestServiceConfigurationProvider另一方面取决于RestEndpointProvider哪个可以是 a或HybrisEndpointProvideraProductServiceEndpointProvider

简而言之,ProductServiceModuleWsSessionModuleWs连接到远程 RESTful Web 服务。特定服务的确切 IP 地址由 RestEndpointProvider 的实现提供。

现在,这就是发生碰撞的地方。随意尝试下面的代码。发生依赖冲突的麻烦地方由注释标记。

理所当然地,编译器抱怨 的两个相互冲突的实现RestEndpointProvider,即HybrisEndpointProviderProductServiceEndpointProvider

正如丹尼尔在他的回答中提到的那样,为避免任何此类冲突,我应该分别连接ProductServiceModuleWsand SessionModuleWs,每个都有自己的具体 RestEndpointProvider 实现,也许像这样

      new ProductServiceModuleWs
      with DefaultRestServiceConfigurationProvider
      with ProductServiceEndpointProvider


      new SessionModuleWs
      with DefaultRestServiceConfigurationProvider
      with HybrisEndpointProvider

但这是我卡住的地方。

现在如何将这两个单独配置的模块注入到ProductDetailsPage避免依赖冲突中,但仍然使用蛋糕模式?

这是示例代码。该代码是自包含的,应该在您的 IDE 中运行。

case class RestEndpoint(url: String, username: Option[String] = None,   password: Option[String] = None)


trait RestEndpointKey {
   def configurationKey: String
}

case object HybrisEndpointKey extends RestEndpointKey { val configurationKey = "rest.endpoint.hybris" }
case object ProductServiceEndpointKey extends RestEndpointKey { val configurationKey = "rest.endpoint.productservice" }


trait ProductDetailsPage {
    self: ProductServiceModule with SessionModule =>
}



trait ProductServiceModule {}

trait SessionModule {}


trait ProductServiceModuleWs extends ProductServiceModule {
    self: RestServiceConfigurationProvider =>
}


trait SessionModuleWs extends SessionModule {
    self: RestServiceConfigurationProvider =>
}


trait RestServiceConfigurationProvider {}

trait DefaultRestServiceConfigurationProvider extends    RestServiceConfigurationProvider {
    self: RestEndpointProvider =>
}


sealed trait RestEndpointProvider {
   def endpointKey: RestEndpointKey
}

trait HybrisEndpointProvider extends RestEndpointProvider {
   val endpointKey = HybrisEndpointKey
}

trait ProductServiceEndpointProvider extends RestEndpointProvider {
   val endpointKey = ProductServiceEndpointKey
}


object Example extends App {

   new ProductDetailsPage
      with ProductServiceModuleWs
      with SessionModuleWs
      with DefaultRestServiceConfigurationProvider
      with HybrisEndpointProvider
      with ProductServiceEndpointProvider /// collision, since HybrisEndpointProvider already defined the endpointKey !!!!! 
   }
}
4

1 回答 1

2

隐式作用域使您可以控制在何处获取值。

在某处,您将按名称在 a 和 b 之间进行选择,无论名称是术语还是类型。

如果您按类型区分它们,那么您可以按类型要求它们。

方便的是,您可以安装一个配置Config[Value1],否则它将与您的示例中的自定义成员混合。

如图所示,您还可以在词法范围内引入隐式。

package conflict

case class Value(s: String)

trait Value1 extends Value
object Value1 {
  implicit val v: Config[Value1] = new Config[Value1] { def value = new Value("hi") with Value1 }
}
trait Value2 extends Value
object Value2 {
  implicit val v: Config[Value2] = new Config[Value2] { def value = new Value("bye") with Value2 }
}

trait Config[A <: Value] { def value: A }

trait Configurator {
  def config[A <: Value : Config]: Config[A] = implicitly[Config[A]]
}

trait Consumer1 { _: Configurator =>
  def f = config[Value1].value
}
trait Consumer2 { _: Configurator =>
  def g = config[Value2].value
}
trait Consumer3 { _: Configurator =>
  def h[V <: Value : Config] = config[V].value
}

object Test extends App with Configurator with Consumer1 with Consumer2 with Consumer3 {
  Console println s"Using $f"
  Console println s"Using $g"
  locally {
    implicit val `my local config` = new Config[Value2] { def value = new Value("hello again") with Value2 }
    Console println s"Using ${h[Value2]}"
  }
}
于 2016-06-21T05:05:55.667 回答