1. Mehr Dynamik bei der
Softwareentwicklung
Skriptsprachen im Vergleich
2. Wer ich bin...
Mein eigener Chef
(Extremer) Softwareentwickler
(Agiler) Coach
Testgetrieben
http://johanneslink.net
3. 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;
}
4. 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;
}
5. 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;
}
6. 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
• Unvorhergesehene Veränderung => Neukompilierung
11. Closures (1)
Programmlogik als vollwertiges Objekt
def sorter = { unsorted ->
unsorted.sort {it.name}
}
def sorted = sorter(people)
//oder so:
def sorted = sorter.call(people)
12. 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
13. Meta-Programmierung
• Erweiterung existierender Klassen
• Hinzufügen von Methoden zur Laufzeit
• Erweiterung / Veränderung der
Sprache durch Eingriff Verwendung
eines „Meta Object Protocol“
14. 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 ->
class MyObject {
def prop
delegate.metaClass.invokeConstructor(*args)
}
void setProperty(String name, value) {
prop = value
assert }new ArrayList()
== ArrayList.create()
def getProperty(String name) {
assert new HashMap() == HashMap.create()
prop
assert new Integer(42) == Integer.create(42)
}
assert new Dimension(1,1) == Dimension.create(1,1)
}
def obj = new MyObject()
obj.firstName = quot;Johannesquot;
obj.lastName = quot;Linkquot;
assert obj.firstName == quot;Linkquot;
15. Vorteile / Nachteile
dynamischer Skriptsprachen
• Vorteile:
• Weniger Code
• Weniger Duplikation
• Verständlichere Abstraktionen
• Mehr Deployment-Optionen
• Nachteile:
• Erschwerte statische Codeanalyse
• Schlechtere Performance
16. 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 ...
• Heute im Visier:
JavaScript, Ruby und Groovy
17. Groovy
• Volle Objekt- • Volle Integration
Orientierung ohne mit der Java-
primitive Typen Plattform
• Optionale statische • Generics
Typisierung
• Annotations
• Closures • Security-Model
• Listen und Maps • Wird (immer) in
als Literale
echten Bytecode
kompiliert
18. Groovy - Vereinfachte
Syntax
System.out.println(quot;Hello World!quot;); //Java style
println 'Hello, World!'
def vorname = 'Stefan'
println quot;$vorname, ich hol' den Wagen.quot;
String lang = quot;quot;quot;Du, ${vorname}, der
Wagen steht eine Zeile weiter.quot;quot;quot;
assert 0.5 == 1/2
class Person {
def name
}
def johannes = new Person(name: quot;Johannesquot;)
assert johannes.name == quot;Johannesquot;
19. 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;
}
20. JavaScript
• Objektorientiert - aber nicht klassenbasiert
• Keine statische Typisierung
• Syntax und ein paar Basis-Objekte an Java
angelehnt
• Läuft (interpretiert) in jedem Browser - im
Detail unterschiedlich
• Enge Verknüpfung mit DOM
• Rhino-JS-Engine in Java SE 6 enthalten
• Standardisiert als ECMAScript
21. JavaScript - Basistypen
var name = 'Johannes';
assertEqual('Johannes Link', name + ' Link');
var eins = 1
assertEqual(2, eins + 1)
assertEqual('11', eins + '1')
var liste = [1, 2, 'drei']
assertEqual('drei', liste[2])
var isBig = eins > 1000
if (isBig) fail()
22. JavaScript - Objekte
var johannes = new Object();
johannes.firstName = quot;Johannesquot;
johannes.lastName = quot;Linkquot;
johannes.name = function() {
return this.firstName + ' ' + this.lastName
}
assertEqual('Johannes Link', johannes.name());
var johannes = {
firstName: 'Johannes', lastName: 'Link',
name: function() {
return this.firstName + ' ' + this.lastName
}
}
23. JavaScript - Prototypen (1)
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
Person.prototype.name = function() {
return this.firstName + ' ' + this.lastName
}
var johannes = new Person('Johannes', 'Link')
assertEqual('Johannes Link', johannes.name())
assertEqual(true, johannes instanceof Person)
25. Ruby
• Sprachumfang und Mächtigkeit
ähnlich wie bei Groovy
• C-Implementierung als Interpreter
• „Berühmt“ durch Ruby on Rails
• Ausschließlich dynamische Typen
• JRuby: Byte-Code und Java-
Integration
26. Ruby - Code
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
end
class Person
def name()
first_name + quot; quot; + last_name
end
end
johannes = Person.new(quot;Johannesquot;, quot;Linkquot;)
assert_equal quot;Johannes Linkquot;, johannes.name
28. Ruby - Mixins
module Debug
def who_am_i
quot;#{self.class.name}quot;
end
end
class Person
include Debug
end
johannes = Person.new(quot;Johannesquot;, quot;Linkquot;)
assert_equal quot;Personquot;, johannes.who_am_i
s = quot;Ein Stringquot;
class Array
s.extend Debug
include Debug
assert_equal quot;Stringquot;, s.who_am_i
end
assert_equal quot;Arrayquot;, [1, 2, 3].who_am_i
29. Ruby Classes are Objects
johannes = Person.new(quot;Johannesquot;, quot;Linkquot;)
assert_equal Person, johannes.class
assert_equal Class, Person.class
assert_equal Class, Class.class
assert_equal Module, Class.superclass
assert_equal Object, Module.superclass
class Class
alias_method :old_new, :new
def new(*args)
puts quot;Objekt wird erzeugt...quot;
old_new(*args)
end
end
35. Warum gerade jetzt?
• Normale Pendelschwingung zwischen
dynamischer und statischer Typisierung
• Frust über Redundanz und wachsende
Komplexität von Java
• Domänen-spezifische Sprachen sind auf
dem Vormarsch
• Multi-Sprachen-Systeme werden hoffähig
• Mehr Wissen über sinnvolle
Einsatzszenarien der unterschiedlichen
Programmiersprachen
37. Einsatzmuster:
Alleskleber
• Applikationen aus bestehenden
Komponenten zusammenbauen
• Java und .NET sind 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)
38. Alleskleber Demo:
RSS Reader
• Zusammenbau von XML Parser, Java
Networking und Swing Widget
Bibliothek, um einen Standard-RSS
Feed darzustellen
39. 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
40. 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);
41. 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
42. DSL-Beispiel (Bernd Schiffer):
Entfernungsberechnung
assert 5001.m == 2000.m + 3.km + 1.m
class Distance {
def meter
assert werteAus('5002 m == 2000 m + 3 km + 2 m')
def plus(other) {
new Distance(meter: meter + other.meter)
def werteAus(String ausdruck) {
}
boolean equals(other) {
Eval.me(
this.meter == other.meter
} ausdruck.replaceAll(quot; (m|km)quot;, {
} alle, einheit -> quot;.${einheit}quot;
}))
class DistanceCategory {
} static def getM(distance) {
new Distance(meter: distance)
}
static def getKm(distance) {
new Distance(meter: distance * 1000)
}
}
43. 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
44. 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)
45. 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
46. 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.
47. Heinzelmännchen:
Mail schicken
def users = [ [name:'Johannes', email:'jux@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; )
} }
48. 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#
49. Weitere Informationen
• Groovy:
• http://groovy.codehaus.org/
• Dierk König et al: „Groovy in Action“
• JavaScript:
• http://developer.mozilla.org/en/docs/JavaScript
• David Flanagan: „JavaScript: The Definitive Guide“
• Microsoft: „ JScript Deviations from ES3“
• Ruby:
• http://www.ruby-lang.org
• Dave Thomas et al.: „Programming Ruby: The
Pragmatic Programmers' Guide“
• http://www.nealford.com/downloads/conferences/
Neal_Ford-Comparing_Groovy_and_JRuby-slides.pdf
50. Zusammenfassung
• Java - und andere Entreprise-Plattformen -
haben Einschränkungen:
• Anpassungen zur Laufzeit
• Präzision im Ausdruck
• Erweiterbarkeit der Sprache
• Dynamische Skriptsprachen sind in diesen
Punkten flexibler und mächtiger
• bringen jedoch andere Probleme mit sich
• Nicht statisch vs dynamisch, sondern...