Scala - NovaTec GmbH

Werbung
Pimp My Scala – Effektive Scala Nutzung
durch typische Entwurfsmuster
OOP 2012
Andreas Tönne, NovaTec GmbH
Direktor R&D der NovaTec GmbH
Standort Frankfurt am Main
© NovaTec
12.06.2013
2
Übersicht
Blitzkurs Scala
Einführung in typische Scala Pattern
 Basierend auf ausgewählten Schlüsselfeatures von Scala
 Beispiele für idiomatisch korrekte Pattern
 Einschließlich Pimp My Library, Cake und Type Classes
Implementierungsskizzen und Erläuterungen
Umfangreiche Beschreibungen und Codebeispiele unter
scala.novatec-gmbh.de
© NovaTec
12.06.2013
3
Design Pattern
Wir alle wissen was Pattern sind?
GoF: Gamma, Helm, Johnson, Vlissides: „Entwurfsmuster“ (Addison-Wesley, 1996)
Pattern sind keine Erfindung sondern einer Entdeckung!
„Keines der Entwurfsmuster … beschreibt neuartige Entwürfe. (…) Trotzdem wurden viele
dieser Entwürfe noch nie dokumentiert. Sie gehören entweder zum Allgemeinwissen …
oder sie sind Teil erfolgreicher … Systeme.“
„Die Entwurfsmuster … sind Beschreibungen zusammenarbeitender Objekte und Klassen,
die maßgeschneidert sind, um ein allgemeines Entwurfsproblem in einem bestimmten
Kontext zu lösen.“
© NovaTec
12.06.2013
4
Design Pattern
Design Pattern entstehen im Kontext der Möglichkeiten einer Programmiersprache
Umgehen unerwünschte Eigenarten der Sprache
Bilden zusammengesetzte wünschenswerte neue Sprachkonstrukte
Dokumentieren idiomatisch guten Einsatz der Sprache
Welche GoF Pattern „passen“ zu Scala?
 Manche Pattern werden „unsichtbar“ durch Scala Sprachmittel ausdrückbar
 Manche Pattern werden zu Anti-Pattern da man das Ursprungsproblem in Scala anders löst
© NovaTec
12.06.2013
5
GoF Pattern – Bewertung für Scala
Unsichtbare Pattern
 Besucher, Brücke, Iterator, Prototyp, Singleton, Strategie
Vereinfachte Pattern
 Abstrakte Fabrik, Beobachter, Dekorierer, Fabrik, Interpreter, Proxy, Zustand,
Zuständigkeitskette
Anti-Pattern
 Befehl, Schablonenmethode
Sonstige Pattern
 Adapter, Erbauer, Fassade, Fliegengewicht, Kompositum, Memento, Vermittler
Ohne Nachweis im Einzelnen!
© NovaTec
12.06.2013
6
Es lohnt sich in Scala nach Quellen neuer Pattern zu suchen
© NovaTec
12.06.2013
7
Grundlagen neuer Scala Pattern
Jedes neue Scala Feature kann Basis neuer Pattern sein
 Komplexe Designs kompakter als Pattern beschreibbar (anstelle eines Frameworks)
 Höherwertige Abstraktionen über der Programmstruktur und Typen erlauben neuartige
Compilezeit Pattern
Scala läuft auf der JVM aber …
 Nicht alle Pattern können 1-1 nach Java übersetzt werden
 Bsp. Cake-Pattern bringt seinen Nutzen zur Compilezeit
 Nicht übersetzbar: Ausnutzen von Modulstruktur und besserem Typsystem
© NovaTec
12.06.2013
8
Scala Blitzkurs
© NovaTec
12.06.2013
9
Scala Motivation
Scala = Scalable Language
Eine Sprache für viele Aufgaben anstelle vieler Sprachen für jeweils eine Aufgabe
Skalierung nach Problemgröße
 Geeignet für kleine Anwendungen (Scripte)
•
Wichtig: Kompaktheit, Schnelligkeit der Entwicklung, Leicht Anpassbarkeit
 Geeignet für große Anwendungen
•
Wichtig: Modularisierung
Skalierung nach Komplexität
 Kontrolle der Komplexität über neue Frameworks, neue Typen und Kontrollstrukturen und
eingebettete DSLs
© NovaTec
12.06.2013
10
Scala Motivation Forts.
Scala kompiliert nach Java Bytecode
Nutzt die JVM und alle darauf bestehenden Tools und Frameworks
Scala = Java + Viel Gutes aus der Theorie der Programmiersprachen
 Kompakterer Code (Typ Inferenz, viele Automatismen statt Boilerplate Code)
 Saubere Integration von Funktionaler Programmierung (Lambda, Closure, Pattern Matching)
 Traits anstelle von Interfaces
 Leistungsfähiges Typsystem
 Modulsystem eingebaut (Trait, Mixin, Abstrakte Member, selftype, Typparameter)
 Voll Interoperabel mit Java
 Geschwindigkeit vergleichbar mit Java
© NovaTec
12.06.2013
11
Scala Tools
scala
 Repl für interaktive Ausführung
 Ausführung von Scala Scripten
 Es gibt wenig was man nicht vollständig in der Repl ausführen kann
scalac
 Kompiliert .scala-Dateien in eine oder mehrere .class-Dateien
 Scala-Dateien Unterschied zu Java:
•
Kein Zwang Klasse = Datei
•
Keine Package-gesteuerte Unterverzeichnisse
•
Scala-Datei ~ Transcript der Repl
– Sequenz von Ausdrücken und Definitionen
© NovaTec
12.06.2013
12
Scala Blitzbeispiel - Kompaktheit
class MyClass {
private int index;
private String name;
private final int id;
}
public MyClass(int index, String name) {
this.index = index;
this.name = name;
}
// getter und setter für index
// getter für name

class MyClass(var index: Int, val name: String, id:Int)
© NovaTec
12.06.2013
13
Scala Blitzbeispiel - Erweiterbarkeit
public BigInteger factorial(BitInteger x)
{
BigInteger fact;
if (x == BigInteger.ZERO)
fact = BigInteger.ONE;
else
fact = x.multiply(factorial(x.subtract(BigInteger.ONE));
return fact;
}

def factorial(x: BigInt): BigInt = if (x == 0) 1 else x * factorial(x - 1)
© NovaTec
12.06.2013
14
Scala – Java Unterschiede
Viele kleine Vereinfachungen, z.B.
 ; am Zeilenende optional
 Leere Argumentklammern optional
 Keine Unterscheidung Statement und Ausdruck
 Implizite Getter und Setter
 Vereinfachte Konstruktoren
 Viele Typangaben optional
Viele leistungsfähige Verbesserungen -> Grundlage der Scala Pattern
 object
 Funktionsobjekte
 Traits
 Implicit Konvertierungen
 Selftype
© NovaTec
12.06.2013
15
Scala Packages und Klassen
package Beispielklasse {
class BoundedPoint(var x:Int, var y:Int) {
val maxVal:Int = 32000
if (x.abs > maxVal || y.abs > maxVal)
throw new IllegalArgumentException("Point too large")
}
def this(d:Int) = this(d, d)
def mirrored : BoundedPoint = {
return new BoundedPoint(y, x)
}
}
© NovaTec
12.06.2013
16
Scala Feature
object Definition
© NovaTec
12.06.2013
17
Scala Feature - Object Definition
Ein object ist die Definition einer Singleton Instanz einer Klasse
object MyObject {
val constant:Int = 2
var variable:String = "Hello World"
def method(param:Int) = variable * param
}
entspricht etwa
class MyObject$ ….
val MyObject = new MyObject$()
© NovaTec
12.06.2013
18
Object Verwendung
Object als Träger von „static“ Variablen und Methoden
 Scala Terminologie: Ein „Companion Object“ einer Klasse
class BoundedPoint….
object BoundedPoint {
val unit = new BoundedPoint(0)
….
}
Object als Modul
 Ein Package ist ein Object (mit impliziter Definition)
Object als Singleton
© NovaTec
12.06.2013
19
Singleton Pattern
Ein unsichtbares Pattern – object ist ein Singleton
Kein Bedarf an getInstance()
object Singleton
{
}
var sharedValue = 42
 Singleton sharedValue
© NovaTec
12.06.2013
20
Factory Pattern - Typsichere Parametrische Factory
Nutzt: Pattern Matching, Sealed Klassenhierarchie
Leistet: Compilezeit-Prüfung der Factory Parameter
sealed abstract class FactoryItem
case object Type1 extends FactoryItem
case object Type2 extends FactoryItem
object Factory {
def createItem ( which:FactoryItem) = which match {
case Type1 => new Item1()
case Type2 => new Item2()
}
}
Factory.createItem(Type2)
© NovaTec
12.06.2013
21
Scala Feature
Funktionen
© NovaTec
12.06.2013
22
Scala Feature - Funktionen
Funktionen sind Objekte
 Können in Variablen gespeichert werden
 Als Parameter übergeben werden
 Zur Laufzeit erzeugt werden
Literalsyntax: (arg1,… argN) => Ausdruck
val nameHasUpperCase = name.exists(char => char.isUpperCase)
boolean nameHasUpperCase = false;
for (Character char : name) {
if (Character.isUpperCase(char)) {
nameHasUpperCase = true;
break;
}
}
© NovaTec
12.06.2013
23
Scala Feature - First-class Funktionen
Funktionen sind Objekte
Implementierung: Objekte mit einer Methode apply(parm1, … parmN)
Methoden können als Funktionsobjekte extrahiert werden
Idiomatische Nutzung:
 Siehe andere funktionale Programmiersprachen (und den Scala Blog)
 Ersetzung von (anonymen) Hilfsklassen als Träger von Methoden in Java Pattern
 Definition von neuen Kontrollstrukturen
© NovaTec
12.06.2013
24
Cell Pattern
Idiomatisch: Objekte erhalten Funktionscharakter wenn es eine apply Methode gibt
Problem: Wir müssen eine Variable per Referenz übergeben
Lösung: Wir definieren ein Value-Objekt mit Funktionsverhalten
class Cell[T](var value:T) {
def apply() = value
def update(v:T) = value=v
}
val cell = new Cell ("")
cell() = "Hello World"
println(cell())
© NovaTec
12.06.2013
25
Iterator Pattern
Iteratoren werden „internalisiert“ und mit dem Iteratorbody parametrisiert
Java:
List<String> result = new ArrayList<String>;
for (String elem: list) {
if (elem.indexOf("World") >= 0)
result.add(elem)
}
Scala:
list.filter(x:String =>x.contains("World"))
Vorteile:
 Multiple Iteratoren gekapselt in Collections
 Collection-Typ des Resultats wird durch die Collection bestimmt und nicht durch den Aufrufer
© NovaTec
12.06.2013
26
Visitor Pattern
Visitor = Iteration über Objektstruktur + Methode für jeden iterierten Elementtyp
Scala Lösung #1: Definiere internen Iterator wie bei einer Collection
 Bsp.: aTree.inorderDo(node:TreeNode => visit Funktionalität)
Scala Lösung #2: Rekursion über Objektstruktur mit Pattern-Matching
abstract class Expr
case class Num(n: Int) extends Expr
case class Sum(l: Expr, r: Expr) extends Expr
case class Prod(l: Expr, r: Expr) extends Expr
def evalExpr(e: Expr): Int = e match {
case Num(n) => n
case Sum(l, r) => evalExpr(l) + evalExpr(r)
case Prod(l, r) => evalExpr(l) * evalExpr(r)
}
© NovaTec
12.06.2013
27
Observer Pattern
Ziel:
 Benachrichtigung von Observern, wenn das Subject sich ändert
 Dynamische Registrierung von Observern
Scala Idiomatisch: Ersetzen einer Observer Klasse durch eine Funktion
case class Event
class Subject {
private var observers:List[(Event, Subject) => Unit] = Nil
def observe(observer: (Event, Subject) => Unit)=
observers = observer :: observers
def notify(evt:Event) = observers.foreach(_(evt, this))
}
subject.observe(e:Event, s:Subject => …..)
© NovaTec
12.06.2013
28
Lazy Funktionen
Ziel: Konditionaler Aufruf einer teuren Funktion
Scala kennt lazy Funktionsparameter
 Werden ohne Auswertung übergeben
 Können selber keine Parameter haben
if (Trace) System.out.println(expensive())

def trace(f: => Any)
{
if (Trace) println(f)
}
trace(expensive())
© NovaTec
12.06.2013
29
Funktionale Pattern – Lazy Lists
Lazy Parameter nur ein Beispiel für viele Pattern aus der funktionalen Welt
Schwerpunkt 1: Besondere Kontrollstrukturen
Stichwort: Continuations
Schwerpunkt 2: Infinite Datenstrukturen
Scala Streams implementieren potentiell unendliche Listen
© NovaTec
12.06.2013
30
Scala Feature
Trait
© NovaTec
12.06.2013
31
Scala Feature - Traits
Traits sind eine Mischung aus abstrakter Klasse und Interface
 „Interface auf Drogen“
 Können Typen, Variablen und abstrakte sowie konkrete Methoden enthalten
Traits mischen Verhalten zu Klassen (Mixin)
trait NoLogger { def log(level:Int, msg:String) = { } }
trait Logger extends NoLogger {
val loggerName = this.getClass.getName
override def log(level:Int, msg:String) =
println("["+level+":"+loggerName+"]" + msg)
}
class C extends SomeClass with Logger ….
© NovaTec
12.06.2013
32
Scala Feature - Traits
class MyClass extends SomeClass with Trait1 with Trait2 with Trait3 {….
Traits überschreiben vorhandene Definitionen in der Reihenfolge Rechts nach Links
 Hat nicht die Problematik mehrfacher Vererbung
 Trait3 überschreibt Trait2 überschreibt Trait1 überschreibt SomeClass
Idiomatisch:
 Traits dienen der statischen Parametrisierung von konkreten Objekten
Siehe das Decorator Pattern
 Traits dienen der Modularisierung
Siehe das Cake Pattern
© NovaTec
12.06.2013
33
Decorator Pattern (Stackable Trait)
Idiomatisch: Verwendung von Trait Mixins anstelle von Delegation
new A with T1
new A with T1 with T2
ergibt "T1 A"
ergibt "T2 T1 A"
Vorteil: Compilezeit-Operation, gut lesbar, Decorator enthalten nur geänderte Methoden
Nachteil: Decorator-Chain nicht dynamisch änderbar
class A {
}
trait T1 {
}
trait T2 {
}
© NovaTec
override def toString = "A"
override def toString = "T1" + super.toString()
override def toString = "T2" + super.toString()
12.06.2013
34
Implicit Konvertierung
© NovaTec
12.06.2013
35
Scala Feature - Implicit Definitionen
Schwieriges Thema und Kandidat für „Gefährlichstes Feature“ in Scala
Sehr wichtiges Feature für besonders fortgeschrittene Scala-Pattern
Dringende Empfehlung: Nutzung auf festgeschriebene Pattern beschränken!
Implizite Definitionen werden automatisch (sofern eindeutig) vom Compiler gesucht und
verwendet
© NovaTec
12.06.2013
36
Scala Feature - Implicit Definitionen
Methoden und Methodenparameter können als implizit gekennzeichnet werden
 Der Compiler löst Typkonflikte auf, indem er nach passenden impliziten Definitionen sucht
 Für die folgenden Pattern genügt etwas Intuition  Mehr Präzision im Scala Blog
Implicit Methoden werden als versteckte Wrapper verwendet
Implicit Parameter werden automatisch mit einem implicit Val aufgefüllt
Idiomatische Nutzung:
 Automatische Typkonvertierung (siehe Adapter Pattern)
 Erweiterung geschlossener Klassen (siehe Pimp My Library Pattern)
 Statisch aufgelöste Dependency Injection (siehe Type Classes Pattern)
© NovaTec
12.06.2013
37
Adapter Pattern
Automatische Konvertierung in eine anonyme Adapter-Unterklasse
Keine Veränderung an den Originalklassen
class Adapter {
def someMethod() = ...
}
class Adaptee {
def differentMethod() = ...
}
val adapter: Adapter = new Adaptee Compiler sucht hier nach impliziter Methode Adaptee=>Adapter
implicit def targetAdapteeConv(x:Adaptee):Adapter = new Adapter {
override def someMethod() = x. differentMethod()}
 val adapter: Adapter = targetAdapteeConv(new Adaptee)
© NovaTec
12.06.2013
38
Pimp My Library Pattern
Problem: Wie erweitere ich eine bestehende API ohne explizite Konvertierungen in
Extension-Unterklassen, ohne Wrapper und ohne Casts?
Lösung: Eine Variante des Adapter-Pattern auf Methodenebene.
 Eine anonyme Klasse mit der gewünschten Methode X über dem vorhandenen Typ T
 Eine implizite Konvertierung von T zur anonymen Klasse für den Aufruf von X
Kriterien:
 Verwende anonyme Klassen weil es idiomatischer ist und nicht den Namensraum zu müllt
Beispiel: „3 :: 52“ als Ausdruck für eine Calendar Instanz mit Uhrzeit
Dazu müssen wir einen :: Operator der Klasse Integer „unterschieben“
Library Beispiel: ArrayOps  Array mit erweiterten Collection-Funktionen
© NovaTec
12.06.2013
39
Pimp My Integer
3::52  Compiler sucht nach einer impliziten Konvertierung von Int zu einem Typ mit ::
Ergebnis: pimp(52).::(52)
implicit def pimp(minutes: Int) = new {
def ::(hours:Int) = {
var cal = java.util.Calendar.getInstance()
cal.set(java.util.Calendar.HOUR_OF_DAY, hours)
cal.set(java.util.Calendar.MINUTE, minutes)
cal
}
}
Vorteile:
 Einfache, lokale Erweiterung
 Leichtgewichtig: wir erzeugen kleine Instanzen mit einer Methode
 Erweiterung wird durch den Scope kontrolliert
© NovaTec
12.06.2013
40
Self Type
© NovaTec
12.06.2013
41
Scala Feature - Selftype
Selftype ist eine Art Cast von this auf einen gewünschten Zieltyp der konkreten Objekte
class MyClass {
self: SomeType => ….
}
MyClass ist dadurch abstrakt (wenn MyClass != SomeType)
Konkrete Unterklasse muss alle Definitionen von SomeType enthalten!
Idiomatische Nutzung:
 Constraint auf die Komposition von Klassen aus Traits
© NovaTec
12.06.2013
42
Scala Feature - Selftype (Anhand eines Beispiels)
trait Hello { def hello = "Hello" }
class Test { self: Test with Hello =>
val helloWorld = hello + "World"
}
// das ist der Selftype
// hello kommt aus Trait Hello
new Test
error: class Test cannot be instantiated because it does not conform to its self-type Test with Hello
val ok = new Test with Hello
ok.helloWorld
 HelloWorld
Schlechtere Alternative:
class Test extends Hello { val helloWorld = hello + "World"}
Warum schlechter?
© NovaTec
12.06.2013
43
Cake Pattern
Problem:
 Dekomposition von Code in Module bei kreuzweise Abhängigkeiten der Module
 Deklarative Komposition des Ergebnis ohne Rückgriff auf Delegation, DI o.ä. Techniken
 Austauschbare Implementierungen der Module
var varModulA:Int
def methModulA() = methModulB(valModulB)
val valModulB:Int
def methModulB(i:Int) = i*varModulA
Zwei Codefragmente mit kreuzweiser Abhängigkeit
Wie kann man diese unabhängig entwickeln und am Ende zusammenfügen?
© NovaTec
12.06.2013
44
Modulare Komposition 1. Versuch
trait ModuleA {
var varModulA:Int
def methModulA() = methModulB(valModulB)
}
trait ModuleB {
}
val valModulB:Int
def methModulB(i:Int) = i*varModulA
Dies ist nicht compilierbar!
Java: Explizite Referenz auf benutzte Module, DI und Delegation
Scala: Composite selftypes und Mixins
© NovaTec
12.06.2013
45
Cake Pattern
Cake bildet das modulare Design des Scala Compilers (Komposition verschiedener Module
zur Konstruktion des Compiler, der REPL und scaladoc)
Konzept:
 Dekomposition in Traits
 Scope-Abhängigkeit der Module ermöglicht durch zusammengesetzte selftypes
 Komposition in einer Klasse
 Statisch typbar wenn in der Klasse alle Abhängigkeiten aufgelöst sind
 Als ob alle Traits manuell in ein großes Modul zusammen kopiert wurden
Vorteile:
 Komposition auch bei zyklischen Abhängigkeiten kein Problem
 Keinerlei Overhead oder erhöhte Komplexität durch die Modularisierung
 Einfacher Zusammenbau von Implementierungsvarianten
© NovaTec
12.06.2013
46
Cake Pattern Skizze
trait ModuleA {
self:ModuleA with ModuleB =>
var varModulA:Int
def methModulA() = methModulB(valModulB)
}
trait ModuleB {
self:ModuleA with ModuleB =>
val valModulB:Int
def methModulB(i:Int) = i*varModulA
}
class Composite extends ModuleA with ModuleB {
….
}
© NovaTec
12.06.2013
47
Type Class Pattern
Wichtiges Pattern im Scala 2.8 Collection-Design
Problemstellung: Wir müssen verschiedene Klassen mit polymorph nutzbaren Methoden (einer
neuen Feature) ausstatten ohne die Klassen zu erweitern.
Lösung: Generalisierte Form des Pimp My Library Pattern.
Idee:

Implementierungen einer Feature für die verschiedenen Klassen

Mache diese als implizite neue Klassendefinition verfügbar

Methoden die das Feature polymorph nutzen wollen, empfangen dieses durch einen impliziten
Methodenparameter
Vorteile:

Vermeidet Vererbung und Modifikation von Klassen

Eine Methode für verschiedene Parametertypen

Einfache Benutzung durch implizites DI der benötigten Konzeptimplementierung

Reichweitensteuerung über Scoping einfach
© NovaTec
12.06.2013
48
Type Class Pattern Beispiel
trait InvertableFeature[CType] { def inverse (obj:CType) : CType }
implicit val intInvertable = new InvertableFeature[Int] {
def inverse (i:Int) = -i
}
implicit val seqInvertable = new InvertableFeature[Seq] {
def inverse (s:Seq) = s.reverse
}
def inverted[CType](obj:CType)(implicit feature:InvertableFeature[CType])
feature.inversed(obj)
=
inverted(42)  inverted[Int](42)(inInvertable)  -42
inverted(1::2::3::Nil)  inverted[Seq](1::2::3::Nil)(seqInvertable)  List(3,2,1)
inverted("HelloWorld")  Compiler Fehler
© NovaTec
12.06.2013
49
Scala Typen
© NovaTec
12.06.2013
50
Scala Feature - Typen
Typen können unabhängig von Klassen- oder Interface Definition definiert werden
Typen können Member von Klassen, Objecten und Traits sein
Typen können als Typparameter (wie in Generics) verwendet werden
Typen können zusammengesetzt und verfeinert werden
Idiomatische Nutzung von Typen in Pattern
 Compilezeit Kontrolle  Typesafe Builder, Phantom Types
 Polymorphie  Duck Typing
© NovaTec
12.06.2013
51
Type Member Beispiel
Typen können als Member einer Klasse, Object oder Trait definiert werden
class AbstractClass {
type T
type U <: Seq[T]
….
}
class RealClass extends AbstractClass {
type T = Int
….
}
Vergleichbar mit Typ-Parameter
Warum beide Optionen?

Reduktion der Zahl der Typparameter

Kapselung der Typ-Parametrisierung
© NovaTec
12.06.2013
52
Struktur Typen
Notation:
{
abstrakte Definitionen
}
Vergleichbar mit einem Ad-Hoc definierten Interface
Kern von "Duck Typing"
Beschreibt einen Typ eines Objektes anhand des notwendigen Verhaltens
type Sizable = { def size: Int }
var s:Sizable = "Hello World"
s = List(1, 2, 3)
© NovaTec
12.06.2013
53
Type Refinement
"Verfeinert" einen bestehenden Typ durch weitere Definitionen
Typname { DEFINITIONEN }
Klassendefinition ist Refinement der Oberklassen und Traits 
class A extends B { DEFINTIONEN }
new A
entspricht new B { DEFINITIONEN }
Kann explizit als Typ verwendet werden
 Zur Veränderung eines Variablentyps
 Zur Veränderung einer neuen Instanz
new Dog { def speak() = bark }
© NovaTec
12.06.2013
54
Phantom Type Pattern
Ziel: Überprüfung des Programmablaufs zur Compilezeit
Beispiel: Sicherstellen dass im Builder Pattern eine konsistente Menge von Builder
Methoden aufgerufen wurde
Lösung:
 Phantom Types als Zustände eines endlichen Automaten zur Compilezeit
 Typparameter oder Typ Member halten den aktuellen "Zustand des Automaten"
 Methodenaufrufe verändern den Zustand über ihren Return-Typ
Phantom Type stammt aus Haskel
Vereinfacht hier: Ein Typ der nicht in Variablen verwendet wird
© NovaTec
12.06.2013
55
Phantom Type Beispiel Skizze
case class SocketState[+S <: State]()
trait State
trait Closed extends State
trait Opened extends State
Closed und Opened sind Phantom Types
trait SocketProcessor {
val openSocket: SocketState[Closed] => SocketState[Opened]
val readSocket: SocketState[Opened] => SocketState[Opened]
val closeSocket: SocketState[Opened] => SocketState[Closed]
}
val result:SocketState[Closed] = (openSocket readSocket closeSocket)(new
SocketState[Closed])
val result:SocketState[Closed] = (openSocket closeSocket readSocket )(new
SocketState[Closed])
© NovaTec
12.06.2013
56
Typesafe Builder Pattern (Phantom Type)
Typsichere Pizza Bäckerei
 Phantom Type beschreibt Zustand der Pizza
 Implizit Konvertierung zur Prüfung des korrekten Endzustands
© NovaTec
12.06.2013
57
Duck Typing
Duck Typing wurde in Anlehnung an ein Gedicht von James Whitcomb Rileys benannt. In
der Kurzform: If it walks like a duck and quacks like a duck, it is a duck.
Ziel: Polymorphe Nutzung von Objekten aus unabhängigen Klassenhierarchien
Verschiedenste Objekte sollen austauschbar sein
 Unabhängig von ihrer Klassenhierarchie
 Sie müssen nur die benötigten Methoden implementieren
Polymorphie von Smalltalk und Ruby
Strategie:
 Structural Type zur Definition der erforderten Methoden
 Pimp my Library zur Anpassung existierenden geschlossener Klassen
Live Beispiel
© NovaTec
12.06.2013
58
Noch nicht müde?
scala.novatec-gmbh.de
Vielen Dank!
© NovaTec
12.06.2013
59
Scala Klassenhierarchie
© NovaTec
12.06.2013
60
Exkurs: Typausdrücke für Instanzen
new A with T1 with T2
Com pound Typ
new A { def toString = "Anonymes A" }
Refinem ent
var duck:{ def toString:String } = new A
Struk turtyp (Duck Typing)
Idiomatisch:
 Sehr häufige Verwendung von Typausdrücken anstelle von Klassendefinitionen
•
Führen intern zu Java anonymen Klassen
 Reduktion der Zahl der konkreten Klassendefinitionen im Vergleich zu Java
 Expliziter Ausdruck der Kompositions-Struktur einer Instanz
© NovaTec
12.06.2013
61
Herunterladen