Advanced Scala - Teil 1

Dependency Injection mit Scala

Heiko Seeberger

Ob man nun das Spring-Framework liebt oder schon wieder hasst, eines muss man ihm hoch anrechnen: Gutes Design im Sinne von loser Kopplung sowie das Prinzip der Dependency Injection gehören heute dank Spring zum Handwerkszeug eines jeden (Java-)Software-Entwicklers. Natürlich bietet sich der Einsatz von Dependency Injection auch für viele Scala-Projekte an und daher stellt sich die Frage, wie wir das am besten hinbekommen können. Dank der “Abwärtskompatibilität” zu Java könnten wir natürlich mit Spring oder Guice arbeiten, aber Scala bietet als mächtige Sprache noch eine andere Möglichkeit. Im Folgenden zeigen wir, wie wir in Scala Dependency Injection ganz ohne Container, XML oder Annotations realisieren können, indem wir nur Sprachmittel einsetzen.

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 dieChannels 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. Dachannels 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 News override protected val channels = new SportsChannel :: new MusicChannel :: Nil override 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 News override protected val channels = new MockSportsChannel :: new MockMusicChannel :: Nil override 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.

Geschrieben von
Heiko Seeberger
Heiko Seeberger
Heiko Seeberger is Fellow at codecentric. He has been a Scala enthusiast ever since he came to know this fascinating language in 2008. Deeply interested in distributed computing he became a contributor to the amazing Akka project in 2010. Heiko has more than 20 years of professional expertise in consulting and software development and regularly shares his expertise in articles and talks. He is also the author of the German Scala book "Durchstarten mit Scala". Heiko tweets as @hseeberger and blogs under heikoseeberger.de.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.