Mehr Dynamik Durch Skriptsprachen - Presentation Transcript
Mehr Dynamik bei der
Softwareentwicklung
Skriptsprachen im Vergleich
Wer ich bin...
Mein eigener Chef
(Extremer) Softwareentwickler
(Agiler) Coach
Testgetrieben
http://johanneslink.net
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;
}
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;
}
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;
}
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
Die Rettung naht:
Dynamische Skriptsprachen
• Scripting
• Dynamik
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(\"Dierk\"));
...
List<Person> sorted = (List<Person>) Eval.x(people,
\"x.sort {it.name}\"
);
Closures (1)
Programmlogik als vollwertiges Objekt
def sorter = { unsorted ->
unsorted.sort {it.name}
}
def sorted = sorter(people)
//oder so:
def sorted = sorter.call(people)
Closures (2)
• Iteration als typischer Anwendungsfall
def namen = [\"Johannes\", \"Frank\", \"Dierk\"]
def auswahl = namen.findAll {name -> name.contains \"n\"}
assert auswahl == [\"Johannes\", \"Frank\"]
List<String> auswahl = new ArrayList<String>();
for (String name : namen) {
if (name.contains(\"n\")) {
auswahl.add(name);
}
}
Äquivalenter Java Code
Meta-Programmierung
• Erweiterung existierender Klassen
• Hinzufügen von Methoden zur Laufzeit
• Erweiterung / Veränderung der
Sprache durch Eingriff Verwendung
eines „Meta Object Protocol“
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 = \"Johannes\"
obj.lastName = \"Link\"
assert obj.firstName == \"Link\"
Vorteile / Nachteile
dynamischer Skriptsprachen
• Vorteile:
• Weniger Code
• Weniger Duplikation
• Verständlichere Abstraktionen
• Mehr Deployment-Optionen
• Nachteile:
• Erschwerte statische Codeanalyse
• Schlechtere Performance
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
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
Groovy - Vereinfachte
Syntax
System.out.println(\"Hello World!\"); //Java style
println 'Hello, World!'
def vorname = 'Stefan'
println \"$vorname, ich hol' den Wagen.\"
String lang = \"\"\"Du, ${vorname}, der
Wagen steht eine Zeile weiter.\"\"\"
assert 0.5 == 1/2
class Person {
def name
}
def johannes = new Person(name: \"Johannes\")
assert johannes.name == \"Johannes\"
Groovy -Closures
def to = \"JUGS\"
def say = { msg ->
msg + \", $to\"
}
assert say(\"Hallo\") == \"Hallo, JUGS\"
to = \"JUG Cologne\"
assert say(\"Hallo\") == \"Hallo, JUG Cologne\"
(1..10).each {
println it
}
[a:1, b:2].each { key, value ->
println \"key: $key, value: $value\"
}
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
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()
JavaScript - Objekte
var johannes = new Object();
johannes.firstName = \"Johannes\"
johannes.lastName = \"Link\"
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
}
}
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)
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
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 + \" \" + last_name
end
end
johannes = Person.new(\"Johannes\", \"Link\")
assert_equal \"Johannes Link\", johannes.name
Ruby - Mixins
module Debug
def who_am_i
\"#{self.class.name}\"
end
end
class Person
include Debug
end
johannes = Person.new(\"Johannes\", \"Link\")
assert_equal \"Person\", johannes.who_am_i
s = \"Ein String\"
class Array
s.extend Debug
include Debug
assert_equal \"String\", s.who_am_i
end
assert_equal \"Array\", [1, 2, 3].who_am_i
Ruby Classes are Objects
johannes = Person.new(\"Johannes\", \"Link\")
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 \"Objekt wird erzeugt...\"
old_new(*args)
end
end
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
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)
Alleskleber Demo:
RSS Reader
• Zusammenbau von XML Parser, Java
Networking und Swing Widget
Bibliothek, um einen Standard-RSS
Feed darzustellen
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
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(\"mitarbeiter\", mitarbeiter);
binding.setVariable(\"bonusClub\", bonusClub);
GroovyShell shell = new GroovyShell(binding);
File script = new File(filename);
BigDecimal bonus = (BigDecimal) shell.evaluate(script);
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
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(\" (m|km)\", {
} alle, einheit -> \".${einheit}\"
}))
class DistanceCategory {
} static def getM(distance) {
new Distance(meter: distance)
}
static def getKm(distance) {
new Distance(meter: distance * 1000)
}
}
Einsatzmuster:
Endoskopische Operation
• Minimal-invasive Eingriffe \"in vivo\"
• 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
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)
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
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.
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: \"$mailUser\", password: \"$password\",
subject: 'Vortrag ist bald fertig') {
from(address: 'johannes.link@googlemail.com')
to(address: user.email)
message( \"\"\"
Hallo ${user.name},
Der Vortrag ist fast fertig:
${new Date().toGMTString()} \"\"\" )
} }
Einsatzmuster: Prototyp
• Machbarkeitsstudien auf der
Zielplattform
• \"Spikes\" 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#
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
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...
0 comments
Post a comment