Da das Einmischen von Merkmalen in Scala statisch erfolgt, erstellen Sie verschiedene Objekte basierend auf bestimmten Bedingungen, wenn Sie die in ein Objekt eingemischten Merkmale variieren möchten.
Nehmen wir ein Beispiel für ein kanonisches Kuchenmuster. Ihre Module sind als Merkmale definiert, und Ihre Anwendung besteht aus einem einfachen Objekt mit einer Reihe von Funktionen
val application =
new Object
extends Communications
with Parsing
with Persistence
with Logging
with ProductionDataSource
application.startup
Jetzt haben alle diese Module nette Selbsttypdeklarationen, die ihre Abhängigkeiten zwischen Modulen definieren, sodass die Zeile nur kompiliert wird, wenn alle Abhängigkeiten zwischen Modulen vorhanden, eindeutig und gut typisiert sind. Insbesondere hat das Persistenzmodul einen Selbsttyp, der besagt, dass alles, was Persistenz implementiert, auch DataSource implementieren muss, ein abstraktes Modulmerkmal. Da ProductionDataSource von DataSource erbt, ist alles großartig und diese Anwendungskonstruktionslinie wird kompiliert.
Was aber, wenn Sie eine andere DataSource verwenden möchten, die zu Testzwecken auf eine lokale Datenbank verweist? Angenommen, Sie können ProductionDataSource nicht einfach mit verschiedenen Konfigurationsparametern wiederverwenden, die aus einer Eigenschaftendatei geladen wurden. In diesem Fall definieren Sie ein neues Merkmal TestDataSource, das DataSource erweitert, und mischen es stattdessen ein. Sie können dies sogar dynamisch basierend auf einem Befehlszeilenflag tun.
val application = if (test)
new Object
extends Communications
with Parsing
with Persistence
with Logging
with TestDataSource
else
new Object
extends Communications
with Parsing
with Persistence
with Logging
with ProductionDataSource
application.startup
Das sieht etwas ausführlicher aus, als wir es gerne hätten, insbesondere wenn Ihre Anwendung ihre Konstruktion auf mehreren Achsen variieren muss. Auf der positiven Seite haben Sie normalerweise nur einen Teil einer solchen bedingten Konstruktionslogik in einer Anwendung (oder im schlimmsten Fall einmal pro identifizierbarem Komponentenlebenszyklus), sodass zumindest die Schmerzen minimiert und vom Rest Ihrer Logik abgegrenzt werden.
Scala ist auch eine Skriptsprache. Ihr Konfigurations-XML kann also ein Scala-Skript sein. Es ist typsicher und keine andere Sprache.
Schauen Sie sich einfach den Start an:
ist nicht so anders als:
java -cp first.jar:second.jar com.example.MyMainClass context.xml
Sie können immer DI verwenden, aber Sie haben noch ein Werkzeug.
quelle
Die kurze Antwort lautet, dass Scala derzeit keine integrierte Unterstützung für dynamische Mixins bietet.
Ich arbeite an dem Autoproxy-Plugin, um dies zu unterstützen, obwohl es derzeit bis zur Version 2.9 angehalten wird, wenn der Compiler über neue Funktionen verfügt, die es viel einfacher machen.
In der Zwischenzeit können Sie fast genau die gleiche Funktionalität erreichen, indem Sie Ihr dynamisch hinzugefügtes Verhalten als Wrapper-Klasse implementieren und anschließend dem Wrap-Member eine implizite Konvertierung hinzufügen.
quelle
Bis das AutoProxy-Plugin verfügbar ist, können Sie den Effekt mithilfe der Delegierung erzielen:
trait Module { def foo: Int } trait DelegatedModule extends Module { var delegate: Module = _ def foo = delegate.foo } class Impl extends Module { def foo = 1 } // later val composed: Module with ... with ... = new DelegatedModule with ... with ... composed.delegate = choose() // choose is linear in the number of `Module` implementations
Aber Vorsicht, der Nachteil ist, dass es ausführlicher ist und Sie bei der Initialisierungsreihenfolge vorsichtig sein müssen, wenn Sie
var
s innerhalb eines Merkmals verwenden. Ein weiterer Nachteil ist, dassModule
Sie die Delegierung nicht so einfach verwenden können , wenn oben pfadabhängige Typen vorhanden sind .Wenn es jedoch eine große Anzahl unterschiedlicher Implementierungen gibt, die variiert werden können, kostet dies wahrscheinlich weniger Code als das Auflisten von Fällen mit allen möglichen Kombinationen.
quelle
In Lift ist etwas in diese Richtung eingebaut. Es besteht hauptsächlich aus Scala-Code, aber Sie haben eine gewisse Laufzeitkontrolle. http://www.assembla.com/wiki/show/liftweb/Dependency_Injection
quelle