Mehr Dynamik Durch Skriptsprachen

  • 3,197 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
3,197
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
0
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 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
  • 7. Die Rettung naht: Dynamische Skriptsprachen • Scripting • Dynamik
  • 8. 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; );
  • 9. Dynamik • Dynamische Typisierung • Closures • Meta-Programmierung
  • 10. Dynamische Typisierung aka „Duck Typing“ def sortByName(unsorted) { unsorted.sort {it.name} } def people = [ new Student(quot;Johannesquot;), new Person(quot;Dierkquot;) ] def sorted = sortByName(people) assert sorted[0].name == quot;Dierkquot; assert sorted[1].name == quot;Johannesquot;
  • 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)
  • 24. JavaScript - Prototypen (2) johannes.name = function() { return 'Joe Cartwright' } assertEqual('Joe Cartwright', johannes.name()) function Student(firstName, lastName) { Person.call(this, firstName, lastName); } Student.prototype = new Person(); Student.prototype.name = function() { return this.firstName; } var jannek = new Student('Jannek', 'Link'); assertEqual('Jannek', jannek.name()); assertEqual('Link', jannek.lastName);
  • 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
  • 27. Ruby - Blocks def three_times(arg) yield arg yield arg yield arg end three_times (quot;JUGSquot;) { |name| puts quot;Hello, quot; + name } def twice(arg, action) action.call(arg) action.call(arg) end print_ciao = proc { |name| puts quot;Ciao, quot; + name } twice quot;JUGSquot;, print_ciao
  • 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
  • 30. Ruby Events Event Hook Methodenaufruf Kernel::system Methode hinzufügen Module#method_added Unterklasse erzeugen Class#inherited ...
  • 31. Vergleich (1) Groovy JavaScript Ruby / JRuby gering, deutlich groß, Verbreitung mittel, wachsend wachsend gleichbleibend wenig eingebaut, Bibliotheken J2EE + GDK zahlreich /+J2EE viele externe Libs dynamisch, Typisierung dynamisch dynamisch optional statisch Objektorien- Objekte + vollständig vollständig tierung primitive Typen Vererbung klassenbasiert Prototyp-basiert klassenbasiert sehr mächtig MOP gering sehr mächtig++ (ab 1.5)
  • 32. Vergleich (2) Groovy JavaScript Ruby / JRuby gut, aber mit Java-Integration optimal gering prinzp. Problemen Laufzeit- kompilierter interpretiert / interpretiert umgebung Bytecode auf JVM JVM Bytecode Tool- mäßig bis gut mäßig mäßig bis gut Unterstützung (IDEA) Typisches Scripting in Java- Ajax im Browser Ruby on Rails Anwendungsfeld Applikationen langsam, besser Performance langsam meist ausreichend werdend http://shootout.alioth.debian.org/
  • 33. Strack Trace Java Thread [main] (Suspended (breakpoint at line 22 in JavaPerson)) JavaPerson.doubleName() line: 22 JavaTester.run() line: 7 Tester.test(Class<Runnable>) line: 16 Tester.main(String[]) line: 6
  • 34. Stack Trace Groovy Thread [main] (Suspended (breakpoint at line 18 in GroovyPerson)) GroovyPerson.doubleName() line: 18 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25 Method.invoke(Object, Object...) line: 585 CachedMethod.invoke(Object, Object[]) line: 95 MetaClassHelper.doMethodInvoke(Object, MetaMethod, Object[]) line: 599 MetaClassImpl.invokeMethod(Class, Object, String, Object[], boolean, boolean) line: 904 MetaClassImpl.invokeMethod(Object, String, Object[]) line: 740 InvokerHelper.invokePogoMethod(Object, String, Object) line: 773 InvokerHelper.invokeMethod(Object, String, Object) line: 753 ScriptBytecodeAdapter.invokeMethodN(Class, Object, String, Object[]) line: 167 ScriptBytecodeAdapter.invokeMethod0(Class, Object, String) line: 195 GroovyTester.run() line: 7 Tester.test(Class<Runnable>) line: 16 Tester.main(String[]) line: 7
  • 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
  • 36. Einsatzmuster für Scripting (von Dierk König) • Alleskleber • Weiches Herz • Kluge Anpassung • Endoskopische Operation • Grenzenlose Offenheit • Heinzelmännchen • Prototyp
  • 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...
  • 51. Zeremonie versus Essenz