Advanced Scala
Willkommen zu Teil 1 der neuen JAXenter-Serie "Advanced Scala" von Heiko Seeberger. Wer schon Erfahrungen mit Scala sammeln konnte, ist hier genau richtig.
Als Beispiel soll uns ein News-Service dienen, der die Messages mehrerer Channels zusammenfasst. Um das Beispiel einfach zu halten beschränken wir uns dabei auf zwei Channels, und zwar auf Sport und Musik (Gibt es etwa Wichtigeres?).
Beginnen wir mit dem Channel, den wir als Trait mit der abstrakten Methode messages definieren, welche eine Sequenz (Liste) von Strings zurückgibt.
trait Channel {def messages: Seq[String]}
Damit könnte unser News-Service in etwa so aussehen:
class News {def latestMessages: Seq[String] =channels flatMap { _.messages take numberOfMessages }}
Wir greifen einfach eine bestimmte Anzahl von Messages, definiert durch numberOfMessages, von jedem Channel ab und fügen diese zusammen . Es bleibt natürlich zu klären, wie wir an die Channels und die numberOfMessages kommen. Ganz offensichtlich wollen wir diese per Dependency Injection beisteuern.
Nun greifen wir tief in die Design-Trickkiste und wenden das sogenannte Cake-Pattern an. Dabei betten wir den News-Service in einen Trait ein, welcher die Dependencies als abstrakte Methoden beisteuert.
trait NewsContext {class News {def latestMessages: Seq[String] =channels flatMap { _.messages take numberOfMessages }}protected val channels: Seq[Channel]protected val numberOfMessages: Int}
Damit sind wir schon nahe am Ziel, denn wir haben schon einmal eine Abstraktion für unsere Dependencies gefunden. Der Trait NewsContext fungiert hier quasi als Scope für den News-Service. Da channels und numberOfMessages ebenfalls im gleichen Scope liegt, können sie im News-Service verwendet werden.
Jetzt müssen wir nur noch unsere Dependencies für unterschiedliche Konfigurationen konkretisieren bzw. injizieren. Dazu bedienen wir uns eines Singleton Objects.
object Configuration extends NewsContext {lazy val news = new Newsoverride protected val channels =new SportsChannel :: new MusicChannel :: Niloverride protected val numberOfMessages = 2}
Diese Konfiguration verwendet die "richtigen" Channels, die folgende "gemockte"; beide sind hier nicht weiter aufgeführt.
object TestConfiguration extends NewsContext {lazy val news = new Newsoverride protected val channels =new MockSportsChannel :: new MockMusicChannel :: Niloverride protected val numberOfMessages = 2}
Die beiden Konfigurationen, als Singleton Objects umgesetzt, dienen also zum "Wiring" der Dependencies und ersetzen im Vergleich mit Spring die XML-Konfigurationsdateien bzw. die Annotations. Ohne jetzt auf Details einzugehen, sei abschließend eines herausgepickt: Mit dem Schlüsselwort lazy können wir sogar die Erzeugung der Objekte auf den Zeitpunkt der Nutzung verschieben.
object Configuration extends NewsContext {...override lazy protected val channels =new SportsChannel :: new MusicChannel :: Nil...}
Der komplette Code dieses Beispiels ist auf github unter http://wiki.github.com/weiglewilczek/demo-scala verfügbar. Fragen oder Kommentare sind jederzeit erwünscht!
In der nächsten Folge wollen wir den Traits treu bleiben und zeigen, wie wir sogar aspektorientierte Programmierung mit reinen Scala-Sprachmitteln umsetzen können.









