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.