2016-06-20 1 views
0

내 질문은 Scala Cake Pattern and Dependency Collisions과 매우 유사합니다. 하지만 Daniel C의 답변에서 제안 된 구체적인 해결책을 찾기 위해 고심하고 있습니다.스칼라 케이크 패턴 : 종속성 충돌을 피하는 방법?

ProductDetailsPage (특성)은 각각 ProductServiceModuleWsSessionModuleWs 구현 개의 독립적 인 서비스 모듈과 ProductServiceModuleSessionModule을 필요

그래서 여기 문제이다.

두 모듈 모두 RestServiceConfigurationProvider을 사용합니다.

RestServiceConfigurationProvider의 경우 구체적인 구현은 DefaultRestServiceConfigurationProvider (atm)입니다. 반면에 DefaultRestServiceConfigurationProvider

짧은에서는 HybrisEndpointProvider 또는 ProductServiceEndpointProvider

될 수 어느 RestEndpointProvider에 의존하고 ProductServiceModuleWsSessionModuleWs 원격 RESTful 웹 서비스에 연결. 특정 서비스의 정확한 IP 주소는 RestEndpointProvider 구현에 의해 제공됩니다.

여기가 충돌입니다. 아래 코드를 사용해보십시오. 의존성 충돌이 발생하는 까다로운 곳은 주석으로 표시됩니다.

로운 때문에, 컴파일러는, 내가 별도로 ProductServiceModuleWsSessionModuleWs를 연결할한다는 RestEndpointProvider, 즉 HybrisEndpointProvider

다니엘은 이러한 충돌을 방지하기 위해, 그의 대답에서 언급 한 바와 같이 ProductServiceEndpointProvider의 두 충돌 구현에 대해 불평 내가 붙어있어 어디 아마 여기 너무

 new ProductServiceModuleWs 
     with DefaultRestServiceConfigurationProvider 
     with ProductServiceEndpointProvider 


     new SessionModuleWs 
     with DefaultRestServiceConfigurationProvider 
     with HybrisEndpointProvider 

그러나 같은 자신의 구체적인 RestEndpointProvider 구현 각각이다.

어떻게이 두 개의 별도로 구성된 모듈을 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 !!!!! 
    } 
} 

답변

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]}" 
    } 
} 
+0

감사합니다. – simou