我的问题与Scala Cake Pattern and Dependency Collisions非常相似。但我正在努力寻找丹尼尔C答案中建议的具体解决方案。
所以这就是问题所在:
ProductDetailsPage
(特质)需要两个独立的服务模块ProductServiceModule
和SessionModule
,分别由ProductServiceModuleWs
和SessionModuleWs
实施。
两个模块都依赖于RestServiceConfigurationProvider
。
对于这个RestServiceConfigurationProvider
,只有一个具体的实现可用:DefaultRestServiceConfigurationProvider
(atm)。
另一方面,DefaultRestServiceConfigurationProvider
取决于RestEndpointProvider
,HybrisEndpointProvider
或ProductServiceEndpointProvider
简而言之,ProductServiceModuleWs
和SessionModuleWs
连接到远程RESTful Web服务。特定服务的确切IP地址由RestEndpointProvider的实现提供。
现在,这就是碰撞发生的地方。请随意尝试下面的代码。发生依赖性冲突的麻烦的地方是注释。
理所当然,编译器抱怨RestEndpointProvider
的两个相互冲突的实现,即HybrisEndpointProvider
和ProductServiceEndpointProvider
正如丹尼尔在他的回答中提到的,为了避免任何此类冲突,我应该分别连接ProductServiceModuleWs
和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 !!!!!
}
}
答案 0 :(得分: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]}"
}
}