SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our User Agreement and Privacy Policy.
SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our Privacy Policy and User Agreement for details.
Successfully reported this slideshow.
Activate your 14 day free trial to unlock unlimited reading.
Problems with Java and how dynamic scripting languages (especially Groovy) tackle some of them. A short introduction to some aspects of Groovy is included.
Problems with Java and how dynamic scripting languages (especially Groovy) tackle some of them. A short introduction to some aspects of Groovy is included.
5.
Java-Frust
public class Person...
public String getName() {
return name;
}
6.
Java-Frust
public class Person...
public String getName() {
return name;
}
public static List<Person> sortByName(Set<Person> people) {
List<Person> sortedResult = new ArrayList<Person>(people);
Comparator<Person> nameComparator = new Comparator<Person>() {
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
};
Collections.sort(sortedResult, nameComparator);
return sortedResult;
}
7.
Java-Frust
public class Person...
public String getName() {
return name;
}
public static List<Person> sortByName(Set<Person> people) {
List<Person> sortedResult = new ArrayList<Person>(people);
Comparator<Person> nameComparator = new Comparator<Person>() {
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
};
Collections.sort(sortedResult, nameComparator);
return sortedResult;
}
public class FinancialInstrument...
public String getName() {
return name;
}
8.
Java-Frust
public class Person implements Namable...
public String getName() {
return name;
}
public static List<Person> sortByName(Set<Person> people) {
List<Person> sortedResult = new ArrayList<Person>(people);
Comparator<Person> nameComparator = new Comparator<Person>() {
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
};
Collections.sort(sortedResult, nameComparator);
return sortedResult;
}
public class FinancialInstrument implements Namable...
public String getName() {
return name;
}
9.
Java-Frust
public class Person implements Namable...
public String getName() {
return name;
}
public class MyHelper...
public static List<Person> Namable> List<T> sortByName(Set<T> namables) {
public static <T extends sortByName(Set<Person> people) {
List<Person> sortedResult = ArrayList<T>(namables);
List<T> sortedResult = new new ArrayList<Person>(people);
Comparator<Person> nameComparator = Comparator<T>() {
Comparator<T> nameComparator = new new Comparator<Person>() {
public int compare(Person T o2) {
public int compare(T o1, o1, Person o2) {
return o1.getName().compareTo(o2.getName());
return o1.getName().compareTo(o2.getName());
}
}
};
};
Collections.sort(sortedResult, nameComparator);
Collections.sort(sortedResult, nameComparator);
return sortedResult;
return sortedResult;
} }
public class FinancialInstrument implements Namable...
public String getName() {
return name;
}
10.
Java-Frust
public class Person...
public String getName() {
return name;
}
public class MyHelper...
public static <T extends Namable> List<T> sortByName(Set<T> namables) {
List<T> sortedResult = new ArrayList<T>(namables);
Comparator<T> nameComparator = new Comparator<T>() {
public int compare(T o1, T o2) {
return o1.getName().compareTo(o2.getName());
}
};
Collections.sort(sortedResult, nameComparator);
return sortedResult;
}
public class FinancialInstrument...
public String getName() {
return name;
}
11.
Java-Frust
public class Person...
public String getName() {
return name;
}
public class MyHelper...
public static <T extends Namable> List<T> sortByName(Set<T> namables) {
List<T> sortedResult = new ArrayList<T>(namables);
Comparator<T> nameComparator = new Comparator<T>() {
public int compare(T o1, T o2) {
Collection sortByName
return o1.getName().compareTo(o2.getName());
self sort: [:each | each name]
};
}
Collections.sort(sortedResult, nameComparator);
return sortedResult;
}
public class FinancialInstrument...
public String getName() {
return name;
}
12.
Typische Java Smells
public class MyHelper...
public static <T extends Namable> List<T> sortByName(Set<T> namables) {
List<T> sortedResult = new ArrayList<T>(namables);
Comparator<T> nameComparator = new Comparator<T>() {
public int compare(T o1, T o2) {
return o1.getName().compareTo(o2.getName());
}
};
Collections.sort(sortedResult, nameComparator);
return sortedResult;
}
13.
Typische Java Smells
public class MyHelper...
public static <T extends Namable> List<T> sortByName(Set<T> namables) {
List<T> sortedResult = new ArrayList<T>(namables);
Comparator<T> nameComparator = new Comparator<T>() {
public int compare(T o1, T o2) {
return o1.getName().compareTo(o2.getName());
}
};
Collections.sort(sortedResult, nameComparator);
return sortedResult;
}
14.
Typische Java Smells
public class MyHelper...
public static <T extends Namable> List<T> sortByName(Set<T> namables) {
List<T> sortedResult = new ArrayList<T>(namables);
Comparator<T> nameComparator = new Comparator<T>() {
public int compare(T o1, T o2) {
return o1.getName().compareTo(o2.getName());
}
};
Collections.sort(sortedResult, nameComparator);
return sortedResult;
}
15.
Typische Java Smells
public class MyHelper...
public static <T extends Namable> List<T> sortByName(Set<T> namables) {
List<T> sortedResult = new ArrayList<T>(namables);
Comparator<T> nameComparator = new Comparator<T>() {
public int compare(T o1, T o2) {
return o1.getName().compareTo(o2.getName());
}
};
Collections.sort(sortedResult, nameComparator);
return sortedResult;
}
16.
Typische Java Smells
public class MyHelper...
public static <T extends Namable> List<T> sortByName(Set<T> namables) {
List<T> sortedResult = new ArrayList<T>(namables);
Comparator<T> nameComparator = new Comparator<T>() {
public int compare(T o1, T o2) {
return o1.getName().compareTo(o2.getName());
}
};
Collections.sort(sortedResult, nameComparator);
return sortedResult;
}
• Variables Verhalten erfordert eigene (anonyme) Klasse
• Incomplete Library Smell
• Statische Typisierung erzwingt viele gemeinsame
Interfaces
• Unvorhergesehenes Veränderung => Neukompilierung
17.
Die Rettung naht:
Dynamische Skriptsprachen
• Scripting
• Dynamik
18.
Scripting
• Knappe und ausdrucksstarke Syntax
• Ausführung von Programmcode ohne
(spürbare) Compilierung
• Selbstständige Skripte
• Aus einer Applikation heraus
19.
Scripting
• Knappe und ausdrucksstarke Syntax
• Ausführung von Programmcode ohne
(spürbare) Compilierung
• Selbstständige Skripte
• Aus einer Applikation heraus
List<Person> people = new ArrayList<Person>();
people.add(new Person(quot;Dierkquot;));
...
List<Person> sorted = (List<Person>) Eval.x(people,
quot;x.sort {it.name}quot;
);
20.
Scripting
• Knappe und ausdrucksstarke Syntax
• Ausführung von Programmcode ohne
(spürbare) Compilierung
• Selbstständige Skripte
• Aus einer Applikation heraus
List<Person> people = new ArrayList<Person>();
people.add(new Person(quot;Dierkquot;));
...
List<Person> sorted = (List<Person>) Eval.x(people,
quot;x.sort {it.name}quot;
);
26.
Closures (2)
• Iteration als typischer Anwendungsfall
def namen = [quot;Johannesquot;, quot;Frankquot;, quot;Dierkquot;]
def auswahl = namen.findAll {name -> name.contains quot;nquot;}
assert auswahl == [quot;Johannesquot;, quot;Frankquot;]
List<String> auswahl = new ArrayList<String>();
for (String name : namen) {
if (name.contains(quot;nquot;)) {
auswahl.add(name);
}
}
Äquivalenter Java Code
27.
Meta-Programmierung
• Erweiterung existierender Klassen
• Hinzufügen von Methoden zur Laufzeit
• Erweiterung / Veränderung der
Sprache durch Verwendung
eines „Meta Object Protocol“
28.
Meta Object Protocol
„A metaobject protocol (MOP) is an interpreter
of the semantics of a program that is open and
extensible“ (Wikipedia)
class MyObject {
def prop
void setProperty(String name, value) {
prop = value
}
def getProperty(String name) {
prop
}
}
def obj = new MyObject()
obj.firstName = quot;Johannesquot;
obj.lastName = quot;Linkquot;
assert obj.firstName == quot;Linkquot;
29.
Meta Object Protocol
„A metaobject protocol (MOP) is an interpreter
of the semantics of a program that is open and
extensible“ (Wikipedia)
Class.metaClass.'static'.create = { Object[] args ->
delegate.metaClass.invokeConstructor(*args)
}
assert new ArrayList() == ArrayList.create()
assert new HashMap() == HashMap.create()
assert new Integer(42) == Integer.create(42)
assert new Dimension(1,1) == Dimension.create(1,1)
30.
Vorteile / Nachteile
dynamischer Skriptsprachen
• Vorteile:
• Weniger Code
• Weniger Duplikation
• Verständlichere Abstraktionen
• Mehr Deployment-Optionen
31.
Vorteile / Nachteile
dynamischer Skriptsprachen
• Vorteile:
• Weniger Code
• Weniger Duplikation
• Verständlichere Abstraktionen
• Mehr Deployment-Optionen
• Nachteile:
• Erschwerte statische Codeanalyse
• Schlechtere Performance
32.
Welche Skriptsprachen
gibt es?
• Verbreiteste Skriptsprachen:
Perl, PHP, Python, Ruby, JavaScript
• Mehr als 200 verschiedene Sprachen
allein auf der Java VM:
Groovy, JRuby, Scala, Bistro ...
33.
Groovy in der
Sprachlandschaft
Text
(c) Dierk König
45.
Groovy Beans & GPath
class Person {
def name
def kinder = []
}
def johannes = new Person(name: quot;Johannesquot;)
assert johannes.name == quot;Johannesquot;
46.
Groovy Beans & GPath
class Person {
def name
def kinder = []
}
def johannes = new Person(name: quot;Johannesquot;)
assert johannes.name == quot;Johannesquot;
johannes.kinder += new Person(name: quot;Jannekquot;)
johannes.kinder += new Person(name: quot;Niklasquot;)
assert johannes.kinder.size() == 2
assert johannes.kinder[0].name == quot;Jannekquot;
47.
Groovy Beans & GPath
class Person {
def name
def kinder = []
}
def johannes = new Person(name: quot;Johannesquot;)
assert johannes.name == quot;Johannesquot;
johannes.kinder += new Person(name: quot;Jannekquot;)
johannes.kinder += new Person(name: quot;Niklasquot;)
assert johannes.kinder.size() == 2
assert johannes.kinder[0].name == quot;Jannekquot;
assert johannes.kinder.name == [quot;Jannekquot;, quot;Niklasquot;]
assert johannes.kinder.kinder*.size() == [0, 0]
48.
Groovy -Closures
def to = quot;JUGSquot;
def say = { msg ->
msg + quot;, $toquot;
}
assert say(quot;Halloquot;) == quot;Hallo, JUGSquot;
to = quot;JUG Colognequot;
assert say(quot;Halloquot;) == quot;Hallo, JUG Colognequot;
(1..10).each {
println it
}
[a:1, b:2].each { key, value ->
println quot;key: $key, value: $valuequot;
}
50.
Java VM <->
Groovy
• Groovy-Klassen können beliebig auf
Java-Klassen zugreifen
• und umgekehrt!
51.
Java VM <->
Groovy
• Groovy-Klassen können beliebig auf
Java-Klassen zugreifen
• und umgekehrt!
• Es werden immer Klassen im Bytecode
erzeugt
• Egal ob Scripts oder Klassen
• Egal ob vorkompiliert oder zur
Laufzeit
52.
Vorkompilierter
Groovy-Code
• groovyc -> *.class files
• groovyc -jointCompilation
behebt Henne-Ei-Problem
• Auslieferung meist als JAR
• groovy-all-x.y.z.jar mitausliefern
56.
Dynamische Evaluation
zur Laufzeit
• Eval.x(users,
quot;x.grep{ it.salary -> 100000 }.address.townquot;);
// me, x(), xy(), xyz()
• GroovyShell: parse, binding, evaluate (String|File|Url)
• GroovyScriptEngine: für viele Sourcen und
Pfadunterstützung
57.
Dynamische Evaluation
zur Laufzeit
• Eval.x(users,
quot;x.grep{ it.salary -> 100000 }.address.townquot;);
// me, x(), xy(), xyz()
• GroovyShell: parse, binding, evaluate (String|File|Url)
• GroovyScriptEngine: für viele Sourcen und
Pfadunterstützung
• GroovyClassLoader (transparente Quellen, Security!)
58.
Dynamische Evaluation
zur Laufzeit
• Eval.x(users,
quot;x.grep{ it.salary -> 100000 }.address.townquot;);
// me, x(), xy(), xyz()
• GroovyShell: parse, binding, evaluate (String|File|Url)
• GroovyScriptEngine: für viele Sourcen und
Pfadunterstützung
• GroovyClassLoader (transparente Quellen, Security!)
• Bean Scripting Framework und JSR-223 (Java 6)
59.
Dynamische Evaluation
zur Laufzeit
• Eval.x(users,
quot;x.grep{ it.salary -> 100000 }.address.townquot;);
// me, x(), xy(), xyz()
• GroovyShell: parse, binding, evaluate (String|File|Url)
• GroovyScriptEngine: für viele Sourcen und
Pfadunterstützung
• GroovyClassLoader (transparente Quellen, Security!)
• Bean Scripting Framework und JSR-223 (Java 6)
• Groovy Spring Beans
60.
Dynamik &
Metaprogrammierung
• Dynamischer Methodenaufruf
und Hooks
• Kategorien
• Methoden zur Laufzeit hinzufügen
61.
Methodenaufrufe in Java
und Groovy
In Java: In Groovy:
• Zur Übersetzungszeit • Zur Ausführungszeit
wird festgelegt, welche wird dynamisch
Methode ermittelt,
auf welcher wie auf einen
Vererbungshierarchie Methodenaufruf
aufgerufen wird reagiert wird
• Zur Ausführungszeit
wird dynamisch
ermittelt,
wie auf einen Property-
Zugriff reagiert wird
62.
Dynamischer Aufruf
class X { def x = new X()
def a = 1 def methodName, propName
def b = 2
def foo () { methodName = 'foo'
3 assert 3 == x.quot;$methodNamequot;()
}
} propName = 'a'
assert 1 == x.quot;$propNamequot;
propName = 'b'
assert 2 == x.quot;$propNamequot;
63.
methodMissing()
class PrintMissingMethods {
def methodMissing(String name, args) {
println quot;missing $name with $argsquot;
args.size()
}
}
x = new PrintMissingMethods()
assert 2 == x.foo(1,2)
-> missing foo with {1, 2}
assert 3 == x.bar('just', 'a', 'test')
-> missing bar with {quot;justquot;, quot;aquot;, quot;testquot;}
64.
Kategorien
class FooCategory { • Kategorie: Klasse
static def foo(List self, arg) { mit statischen
self + [arg] Methoden
}
• Erstes Argument ist
static def foo(String self, arg) { Empfänger der
self + arg Methode
}
• Weitere Argumente
}
sind Parameter der
Methode
use(FooCategory) {
assert [123].foo(456) == [123, 456] • Verwendung der
assert '123'.foo('456') == '123456' Kategorie mittels
} use(...) { ... }
65.
Methoden zur Laufzeit
hinzufügen
String.metaClass.upperCaseCount = { -> • Sogenannte
(delegate =~ /[A-Z]/).size() ExpandoMetaClass
}
• Methode definieren:
assert 1 == quot;Onequot;.upperCaseCount()
Property der
assert 2 == quot;TWoquot;.upperCaseCount()
Metaklasse auf eine
assert 3 == quot;ThReEquot;.upperCaseCount()
Closure setzen,
fertig!
Integer.metaClass.'static'.
answerToEverything = { -> 42 } • Auch
assert 42 == Integer.answerToEverything() Klassenmethoden
können definiert
werden
ExpandoMetaClass.enableGlobally()
Object.metaClass.tate = { -> 42 } • Auch Vererbung
assert 42 == new X().tate() funktioniert
66.
Was gibt es noch?
• XML-Support eingebaut
• Builder-Konzept (z.B. SwingBuilder)
• Nahtlose Ant-Integration
• Testen leicht gemacht
• Web-Applikationen mit Grails
(= Spring + Hibernate + Groovy)
• ...
67.
Wie entwickle ich
Groovy-Programme?
• groovysh
• groovyConsole
• IDE-Support
• IDEA IntelliJ
• Eclipse
• NetBeans
69.
Einsatzmuster:
Alleskleber
• Applikationen aus bestehenden
Komponenten zusammenbauen
• Java ist gut geeignet für stabile
Infrastruktur: Middleware,
Frameworks, Widget Sets, Services
• Scripting ist gut geeignet für flexible
View- und Controller-Schichten (z.B.
Grails)
70.
Alleskleber Demo:
RSS Reader
• Zusammenbau von XML Parser, Java
Networking und Swing Widget
Bibliothek, um einen Standard-RSS
Feed darzustellen
71.
Einsatzmuster:
Weiches Herz
• Fachliche Modelle auslagern bei
vorgegebenem Java-Applikationsgerüst
• Fachlichen Erkenntnisfortschritt
ermöglichen: Entitäten, Beziehungen
und Verhalten bleiben durch Scripting
flexibel
• Verhaltensänderung zur Laufzeit
73.
Weiches Herz Beispiel:
Bonusberechnung
umsatz = mitarbeiter.umsatz
switch(umsatz / 1000) {
case 0..100: return umsatz * 0.04
case 100..200: return umsatz * 0.05
case {it > 200}:
bonusClub.add(mitarbeiter)
return umsatz * 0.06
}
Binding binding = new Binding();
binding.setVariable(quot;mitarbeiterquot;, mitarbeiter);
binding.setVariable(quot;bonusClubquot;, bonusClub);
GroovyShell shell = new GroovyShell(binding);
File script = new File(filename);
BigDecimal bonus = (BigDecimal) shell.evaluate(script);
74.
Einsatzmuster:
Kluge Anpassung
• Konfigurationen mit Ausführungs-Logik als
Ersatz für XML-Konfigurationen
• Mit Referenzen, Schleifen, Bedingungen,
Vererbung, Ausführungslogik,
Umgebungsermittlung, ...
• Typischer Anwendungsfall für domänen-
spezifische Sprachen (DSLs)
• Veränderungen durch den Experten
76.
DSL-Beispiel (von Bernd Schiffer):
Entfernungsberechnung
assert 5001.m == 2000.m + 3.km + 1.m
class Meter {
def meter
Meter(meter) {
this.meter = meter
}
def plus(kilometer) {
new Meter(meter + kilometer.meter)
}
boolean equals(other) {
this.meter == other.meter
}
}
class Kilometer {
def meter
Kilometer(kilometer) {
meter = kilometer * 1000
}
}
class Distance {
static def getM(distance) {
new Meter(distance)
}
static def getKm(distance) {
new Kilometer(distance)
}
}
77.
DSL-Beispiel (von Bernd Schiffer):
Entfernungsberechnung
assert 5001.m == 2000.m + 3.km + 1.m
class Meter {
def meter
assert werteAus('5002 m == 2000 m + 3 km + 2 m')
Meter(meter) {
this.meter = meter
}
def plus(kilometer) {
def werteAus(String anweisung) {
new Meter(meter + kilometer.meter)
}
Eval.me(
boolean equals(other) {
anweisung.replaceAll(quot; (m|km)quot;, {
}
this.meter == other.meter
}
alle, einheit -> quot;.${einheit}quot;
class Kilometer {
})) def meter
Kilometer(kilometer) {
}
meter = kilometer * 1000
}
}
class Distance {
static def getM(distance) {
new Meter(distance)
}
static def getKm(distance) {
new Kilometer(distance)
}
}
78.
Einsatzmuster:
Endoskopische Operation
• Minimal-invasive Eingriffe quot;in vivoquot;
• Viele Notwendigkeiten für Anpassungen
ad-hoc Anfragen sind nicht vorhersehbar
• Schlüsselloch für die Live Ausführung von
Scripts schaffen, z.B. in einem speziellen
Servlet
• Unglaublich wertvoll für Produkt-Support,
Fehleranalyse, Hot-Fixes, Notfälle
80.
Endoskopische
Operation: im Servlet
Probleme mit der Datenbank Verbindung?
def ds = Config.dataSource
ds.connection = new DebugConnection(ds.connection)
81.
Endoskopische
Operation: im Servlet
Probleme mit der Datenbank Verbindung?
def ds = Config.dataSource
ds.connection = new DebugConnection(ds.connection)
Gefährliche Benutzer rauswerfen
users = servletContext.getAttribute('users')
bad = users.findAll { user ->
user.cart.items.any { it.price < 0 }
}
servletContext.setAttribute('users', users - bad)
82.
Einsatzmuster:
Grenzenlose Offenheit
• Jede Zeile Code wird änderbar
• Manchmal sind die vorgesehenen
Variationspunkte nicht ausreichend
• Einfache Änderungen ohne
langwieriges Setup für Kompilation
und Deployment
• Perl, PHP, Python, etc. machen es vor
83.
Einsatzmuster:
Heinzelmännchen
• Repetitive Aufgaben automatisieren
• Automatisierter Build, kontinuierliche
Integration, Deployment,
Installationen, Server-Überwachung,
Reports, Statistiken, Erzeugen von
Dokumentation, funktionale Tests
uvm.
• Anwendungen mit Ant, Maven und Co.
84.
Heinzelmännchen:
Mail schicken
def users = [ [name:'Johannes', email:'jl@johanneslink.net'],
[name:'Teilnehmer', email:'wer@wo.net']]
def ant = new groovy.util.AntBuilder()
for (user in users) {
ant.mail(mailhost: 'smtp.googlemail.com', mailport: '465',
ssl: 'true', user: quot;$mailUserquot;, password: quot;$passwordquot;,
subject: 'Vortrag ist bald fertig') {
from(address: 'johannes.link@googlemail.com')
to(address: user.email)
message( quot;quot;quot;
Hallo ${user.name},
Der Vortrag ist fast fertig:
${new Date().toGMTString()} quot;quot;quot; )
} }
85.
Einsatzmuster: Prototyp
• Machbarkeitsstudien auf der
Zielplattform
• quot;Spikesquot; für technologische oder
algorithmische Ideen mit besserer
Ausdrucksmächtigkeit, schnellerem
Feedback und besseren
Analysemöglichkeiten
• Wahlmöglichkeit für spätere (Teil-)
Portierung nach Java / C#
86.
Weitere Informationen
• http://groovy.codehaus.org/
• http://grails.codehaus.org/
• Dierk König et al: „Groovy in Action“
87.
Zusammenfassung
• Die Programmiersprache Java hat
Einschränkungen:
• Anpassungen zur Laufzeit
• Präzision im Ausdruck
• Erweiterbarkeit der Sprache
• Groovy als dynamische Skriptsprache ist in
diesen Punkten flexibler und mächtiger
• Groovys Ziel ist die best mögliche Integration in
die Java-Plattform
• Es existieren typische Einsatzmuster
88.
Fragen und
Anmerkungen?
http://www.slideshare.net/jlink/
mehr-dynamik-mit-groovy/