Problems with Java and how dynamic scripting languages (especially Groovy) tackle some of them. A short introduction to some aspects of Groovy is included.
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
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;
);
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 ...
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
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#
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