2016-06-20 7 views
0

Meine Frage ist sehr ähnlich zu Scala Cake Pattern and Dependency Collisions. Aber ich bemühe mich, eine konkrete Lösung zu finden, wie sie in Daniel Cs Antwort vorgeschlagen wird.Scala Cake Pattern: Wie Dependency Collisions vermeiden?

So, hier ist das Problem:

A ProductDetailsPage (Zug) erfordert zwei unabhängige Servicemodule ProductServiceModule und SessionModule, implementiert durch ProductServiceModuleWs und SessionModuleWs sind.

Beide Module basieren auf einem RestServiceConfigurationProvider.

Für diese RestServiceConfigurationProvider ist nur eine konkrete Implementierung verfügbar: DefaultRestServiceConfigurationProvider (atm).

Die DefaultRestServiceConfigurationProvider auf der anderen Seite hängt von der RestEndpointProvider, die entweder ein HybrisEndpointProvider oder sein kann ProductServiceEndpointProvider

Kurz, ProductServiceModuleWs und SessionModuleWs verbinden Dienste Remote-REST-konformen Web. Die genaue IP-Adresse des bestimmten Dienstes wird von einer Implementierung des RestEndpointProvider bereitgestellt.

Nun, das ist, wo die Kollisionen passiert. Probieren Sie den folgenden Code aus. Der störende Ort, an dem die Abhängigkeitskollision stattfindet, ist durch einen Kommentar markiert.

Zu Recht rügt der Compiler über die beiden widerstreitenden Implementierungen der RestEndpointProvider, nämlich HybrisEndpointProvider und ProductServiceEndpointProvider

Als Daniel in seiner Antwort erwähnt, keine solche Kollisionen zu vermeiden, habe ich separat die ProductServiceModuleWs und SessionModuleWs verdrahten sollte, jede mit ihrer eigenen konkreten RestEndpointProvider Implementierung, vielleicht wie so

 new ProductServiceModuleWs 
     with DefaultRestServiceConfigurationProvider 
     with ProductServiceEndpointProvider 


     new SessionModuleWs 
     with DefaultRestServiceConfigurationProvider 
     with HybrisEndpointProvider 

Aber hier ist, wo ich stecken geblieben.

Wie können diese zwei separat konfigurierten Module jetzt in die ProductDetailsPage injiziert werden, wobei Abhängigkeitskollisionen vermieden werden, aber trotzdem das Kuchenmuster verwendet wird?

Hier ist der Beispielcode. Der Code ist eigenständig und sollte in Ihrer IDE ausgeführt werden.

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 !!!!! 
    } 
} 

Antwort

2

Impliziter Bereich gibt Ihnen eine gewisse Kontrolle darüber, wo Sie Werte abrufen.

Irgendwo wählen Sie zwischen a und b mit Namen, ob der Name ein Begriff oder ein Typ ist.

Wenn Sie sie nach Typ unterscheiden, können Sie nach Typ fragen.

Die Bequemlichkeit ist, dass Sie eine Konfiguration für Config[Value1] installieren können, die sonst ein Mix-in mit einem benutzerdefinierten Mitglied wie in Ihrem Beispiel wäre.

Wie gezeigt, können Sie auch implicits im lexikalischen Bereich einführen.

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

Ich habe viel von Ihrem Beispiel gelernt, danke. – simou

Verwandte Themen