0

私の質問はScala Cake Pattern と Dependency Collisionsに非常に似ています。しかし、ダニエル C の回答で提案されているように、具体的な解決策を見つけるのに苦労しています。

だからここに問題があります:

A ProductDetailsPage(特性) には、2 つの独立したサービス モジュールProductServiceModuleおよびが必要で、それぞれ およびSessionModuleによって実装されます。ProductServiceModuleWsSessionModuleWs

両方のモジュールは に依存していRestServiceConfigurationProviderます。

このRestServiceConfigurationProviderの場合、使用可能な具体的な実装は 1 つだけです: DefaultRestServiceConfigurationProvider(atm)。

一方、は、どちらが aまたは aであるかDefaultRestServiceConfigurationProviderによって異なります。RestEndpointProviderHybrisEndpointProviderProductServiceEndpointProvider

つまり、リモート RESTful Web サービスに接続しますProductServiceModuleWsSessionModuleWs特定のサービスの正確な IP アドレスは、RestEndpointProvider の実装によって提供されます。

さて、ここで衝突が発生します。以下のコードを試してみてください。依存関係の衝突が発生する厄介な場所はコメントでマークされています。

RestEndpointProvider当然のことながら、コンパイラは の 2 つの競合する実装、つまりHybrisEndpointProviderとについて不平を言います。ProductServiceEndpointProvider

ダニエルが彼の答えで述べたように、そのような衝突を避けるために、それぞれ独自の具体的な RestEndpointProvider 実装を使用して、ProductServiceModuleWsと を別々に配線する必要があります。SessionModuleWs

      new ProductServiceModuleWs
      with DefaultRestServiceConfigurationProvider
      with ProductServiceEndpointProvider


      new SessionModuleWs
      with DefaultRestServiceConfigurationProvider
      with HybrisEndpointProvider

しかし、ここで行き詰まりました。

ProductDetailsPageこれらの 2 つの個別に構成されたモジュールを、依存関係の衝突を回避しながらケーキ パターンを利用しながら、どのように挿入することができるでしょうか?

これがサンプルコードです。コードは自己完結型であり、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 に答える