2013-12-12 6 views
6

Nehmen wir an, wir haben eine Eigenschaft T. Was ist der beste Weg, das zu erreichen, folgende:Kurzer Weg zur Durchsetzung der Fabrik in Scala

  • Jeder, der eine Implementierung von T schreibt sollte eine Möglichkeit zu schaffen gezwungen werden, die eine parameterfreie Initialisierung von T erlaubt, dh wir wahrscheinlich die Umsetzung durchzusetzen eine konfigurierbare Fabrik.
  • Alle Logik/Daten, die nur von den tatsächlichen Initialisierungsparametern (einer bestimmten Implementierung A von T) abhängen, sollten zentral gehandhabt/gespeichert werden, sollten aber in der Fabrik und A verfügbar sein.

Die einfachste/prägnante Art und Weise sehe ich (ungefähr) zu erreichen, wäre ein Merkmal für eine Fabrik und Link hinzufügen T zu diesem Werk:

trait T { 
    val factory: TFactory 
} 
trait TFactory { 
    def build(): T 
    val description: String // example for logic/data that only depend on the parameters 
} 

// example implementation: 
class A(val factory: AFactory, paramA: Int, paramB: Int, paramC: Int) extends T 

class AFactory(paramA: Int, paramB: Int, paramC: Int) extends TFactory { 
    def build = new A(this, paramA, paramB, paramC) 
    val description = f"$paramA $paramB $paramC" 
} 

Offensichtlich ist diese „erzwingt nicht wirklich "Die Implementierung einer Fabrik (solange es eine alternative Implementierung gibt) und offensichtlich ist es möglich, Instanziierungen von A zu generieren, die mit einem" falschen "TFactory verknüpft sind. Was ich an diesem Ansatz auch nicht mag, ist die Wiederholung der Initialisierungsparameter. Ich erstelle oft noch eine Klasse AParams, die wiederum alle Parameter umschließt (zum Beispiel um das Hinzufügen neuer Parameter zu erleichtern). Somit habe ich drei Klassen, die für dieses einfache Problem eine Menge Standard sind.

Meine Frage ist, ob es einen (vielleicht völlig) anderen Ansatz gibt, der dieselben primären Ziele erreicht, aber prägnanter ist?

Antwort

1

Ich bin mir nicht ganz sicher, ob ich die volle Absicht Ihrer Anforderungen bekomme, aber was halten Sie von diesem Verhalten?

trait TFactory{ 
    def build():T 
    val description:String 
} 

trait T extends TFactory 

//can't declare A without build and not make it abstract 
class A(paramA: Int, paramB: Int, paramC: Int) extends T { 
    def build = new A(paramA, paramB, paramC) 
    val description = f"$paramA $paramB $paramC"  
} 

val a1 = new A(1, 4, 5) 
val a2 = a1.build() 

//We can give ourselves as a factory to something that expects TFactory 
val factory:TFactory = a1 
val a_new = factory.build() 

//More likely we can just give our build method 
def func(f:()=>T) = { 
    val new_t = f() 
    new_t 
} 
val a_newer = func(a1.build) 


println(a1 +": " + a1.description) 
println(a2 +": " + a2.description) 
println(a_new +": " + a_new.description) 
println(a_newer +": " + a_newer.description) 

Ausgang:

[email protected]: 1 4 5 
[email protected]: 1 4 5 
[email protected]: 1 4 5 
[email protected]: 1 4 5 
+0

Definitiv eine interessante Idee, danke! Ein praktisches Problem, das ich sehe, ist, dass ich intuitiv die Vorstellung verliere, dass eine Fabrik leicht ist, während eine tatsächliche Implementierung von "T" eher schwergewichtig ist. Oftmals beinhaltet die Konstruktion eines echten "A" in meinen Anwendungsfällen eine große Menge an Initialisierung, was zu einer Instanz von "A" mit einem beträchtlichen Speicherbedarf führt. Ich würde wahrscheinlich oft mit einer Instanz von "A" enden, die ich nie wirklich in ihrem eigentlichen Sinn verwenden würde, ein "T" zu sein, sondern einfach als Fabrik mit einem unnötigen Overhead. Aber vielleicht ist das der Preis für die Vereinfachung. – bluenote10

+0

Es sieht so aus, als müssten Sie die Konstruktorparameter von T in Ihrer Fabrik verwenden. Ich sehe also nicht, wie Sie eine solche Fabrik haben könnten, ohne eine Instanz von T. Wenn Sie nicht die Parameter in a eingeben Klasse, die sowohl einer Fabrik als auch einem Konstruktor der Unterklasse von T zur Verfügung gestellt werden könnte. –

+0

Speaking purefly aus Designperspektive Ich bin mir nicht sicher, ob Sie eine solche Fabrik erzwingen müssen, wenn es nicht die einzige Möglichkeit ist, T zu bauen.Vermutlich haben Sie eine Methode, die eine Factory benötigt, die nur eine Funktion sein kann: 'MakeTsAndDoUsefulThings (factory:() => T)'. Dann in der Zukunft, wenn ich als ein Client von Ihnen Code "SonOfT" machen und finden, dass ich diese Funktion verwenden muss, werde ich eine Fabrik für "SonOfT", wahrscheinlich in meinem Begleiter-Objekt und diesen Aufruf machen: MakeTsAndDoUsefulThings (SonOfT.defaultFactory) ' Wenn ich nie diese Methode verwenden muss, werde ich nie die Fabrik machen müssen, die in Ordnung scheint. –

1

einen Darstellungsart Parameter hinzufügen:

trait Factory[Prod] { 
    def build(): Prod 
} 

trait Prod[Repr] { 
    def factory: Factory[Repr] 
} 

Oder, wenn Sie wollen "erzwingen", dass der Typ gleich bleibt (ich würde das nicht tun Wenn Sie nichts daraus gewinnen):

trait Prod[Repr <: Prod[Repr]] { 
    def factory: Factory[Repr] 
} 

Dann:

case class AConfig(a: Int, b: Int) 

case class A(config: AConfig) extends Prod[A] { 
    def factory = AFactory(config) 
} 

case class AFactory(config: AConfig) extends Factory[A] { 
    def build() = A(config) 
} 

val f0 = AFactory(AConfig(1, 2)) 
val p0 = f0.build() 
val f1 = p0.factory 
val p1 = f1.build() 
assert(p0 == p1) 
Verwandte Themen