SlideShare ist ein Scribd-Unternehmen logo
1 von 88
Groovy
Mehr Dynamik bei der Java-
      Entwicklung
Wer ich bin...
Wer ich bin...
    Mein eigener Chef

    (Extremer) Softwareentwickler

    (Agiler) Coach

    Testgetrieben

    http://johanneslink.net
Java-Frust
Java-Frust
public class Person...
    public String getName() {
          return name;
    }
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;
}
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 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;
    }
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) {
    
 
    
   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...
      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;
    }
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;
    }
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;
    }
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;
    }
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
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
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;
);
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;
);
Dynamik


• Dynamische Typisierung
• Closures
• Meta-Programmierung
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;
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
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;]
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
Meta-Programmierung


• Erweiterung existierender Klassen
• Hinzufügen von Methoden zur Laufzeit
• Erweiterung / Veränderung der
  Sprache durch 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 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;
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)
Vorteile / Nachteile
dynamischer Skriptsprachen
• Vorteile:
  • Weniger Code
  • Weniger Duplikation
  • Verständlichere Abstraktionen
  • Mehr Deployment-Optionen
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 ...
Groovy in der
Sprachlandschaft


      Text




             (c) Dierk König
Groovy
Groovy
• Volle Objekt-
  Orientierung ohne
  primitive Typen
Groovy
• Volle Objekt-
  Orientierung ohne
  primitive Typen
• Optionale statische
  Typisierung
Groovy
• Volle Objekt-
  Orientierung ohne
  primitive Typen
• Optionale statische
  Typisierung
• Closures
Groovy
• Volle Objekt-
  Orientierung ohne
  primitive Typen
• Optionale statische
  Typisierung
• Closures
• Listen und Maps
  als Literale
Groovy
• Volle Objekt-         • Volle Integration
  Orientierung ohne       mit der Java-
  primitive Typen         Plattform
• Optionale statische     • Generics
  Typisierung
                          • Annotations
• Closures                • Security-Model
• Listen und Maps
  als Literale
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(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

def printSize(obj) {
    print obj?.size()
}
printSize(null)
-> null
Reguläre Ausdrücke
assert (quot;Hello World!quot; =~ /Hello/) // Find-Operator

assert (quot;Hello World!quot; ==~ /Hellob.*/) // Match-Operator

def pattern = ~/.*ll.*/ // java.util.regex.Pattern.compile(quot;.*ll.*quot;)
assert [quot;halloquot;, quot;helloquot;, quot;holaquot;].grep(pattern) == [quot;halloquot;, quot;helloquot;]

def coded = quot;Hallo, JUG Cologne!quot;.replaceAll(/w+/){ match ->
    match.size()
}
assert coded == quot;5, 3 7!quot;
Listen, ...
def leer = []
def voll = [1, 2, 'JUGC']

assert voll + voll == voll * 2

assert voll[0]     == 1
assert voll[0..1] == [1, 2]

voll[0..1] = [0, 1, 2, 3]
assert voll == [0, 1, 2, 3, 'JUGC']
assert voll[2..-1] == [2, 3, 'JUGC']
... Maps und Ranges
def leer = [:]
def voll = [a: 1, b: 2]

assert voll['a'] == 1
assert voll.a    == 1

voll.a = 2
assert voll == [a: 2, b: 2]

def inclusive = 'a'..'z'
inclusive.each {c -> print c}

def exclusive = 0..<10
for (i in exclusive) {print i}
Groovy Beans & GPath
class Person {

    def name

    def kinder = []
}
def johannes = new Person(name: quot;Johannesquot;)
assert johannes.name == quot;Johannesquot;
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;
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]
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;
}
Java VM <->
  Groovy
Java VM <->
  Groovy
• Groovy-Klassen können beliebig auf
  Java-Klassen zugreifen
  • und umgekehrt!
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
Vorkompilierter
       Groovy-Code

• groovyc -> *.class files
• groovyc -jointCompilation
  behebt Henne-Ei-Problem

• Auslieferung meist als JAR
• groovy-all-x.y.z.jar mitausliefern
Dynamische Evaluation
    zur Laufzeit
Dynamische Evaluation
    zur Laufzeit
• Eval.x(users,
     quot;x.grep{ it.salary -> 100000 }.address.townquot;);
  // me, x(), xy(), xyz()
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)
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
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!)
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)
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
Dynamik &
 Metaprogrammierung

• Dynamischer Methodenaufruf
  und Hooks

• Kategorien
• Methoden zur Laufzeit hinzufügen
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
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;
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;}
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(...) { ... }
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
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)
• ...
Wie entwickle ich
  Groovy-Programme?
• groovysh
• groovyConsole
• IDE-Support
 • IDEA IntelliJ
 • Eclipse
 • NetBeans
Einsatzmuster für Scripting
    (von Dierk König)
• Alleskleber
• Weiches Herz
• Kluge Anpassung
• Endoskopische Operation
• Grenzenlose Offenheit
• Heinzelmännchen
• Prototyp
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)
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
}
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);
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 (von Bernd Schiffer):
    Entfernungsberechnung
 assert 5001.m == 2000.m + 3.km + 1.m
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)
 
        }
 }
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)
 
        }
 }
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
Endoskopische
Operation: im Servlet
Endoskopische
    Operation: im Servlet
Probleme mit der Datenbank Verbindung?
def ds = Config.dataSource
ds.connection = new DebugConnection(ds.connection)
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:'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; )
}   }
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#
Weitere Informationen


• http://groovy.codehaus.org/
• http://grails.codehaus.org/
• Dierk König et al: „Groovy in Action“
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
Fragen und
Anmerkungen?
http://www.slideshare.net/jlink/
  mehr-dynamik-mit-groovy/

Weitere ähnliche Inhalte

Andere mochten auch

Agile08: Test Driven Ajax
Agile08: Test Driven AjaxAgile08: Test Driven Ajax
Agile08: Test Driven Ajaxjlink
 
Blogging As Pedagogic Practice Across the Curriculum
Blogging As Pedagogic Practice Across the CurriculumBlogging As Pedagogic Practice Across the Curriculum
Blogging As Pedagogic Practice Across the CurriculumKenneth Ronkowitz
 
Technology Ethics: An Oxymoron?
Technology Ethics: An Oxymoron?Technology Ethics: An Oxymoron?
Technology Ethics: An Oxymoron?Kenneth Ronkowitz
 
Technical Writing Across Disciplines
Technical Writing Across DisciplinesTechnical Writing Across Disciplines
Technical Writing Across DisciplinesKenneth Ronkowitz
 
Von Java Zu Groovy
Von Java Zu GroovyVon Java Zu Groovy
Von Java Zu Groovyjlink
 
2013: The Beginning of the End of the University
2013: The Beginning of the End of the University2013: The Beginning of the End of the University
2013: The Beginning of the End of the UniversityKenneth Ronkowitz
 
Rubrics: Transparent Assessment in Support of Learning
Rubrics: Transparent Assessment in Support of LearningRubrics: Transparent Assessment in Support of Learning
Rubrics: Transparent Assessment in Support of LearningKenneth Ronkowitz
 

Andere mochten auch (10)

Agile08: Test Driven Ajax
Agile08: Test Driven AjaxAgile08: Test Driven Ajax
Agile08: Test Driven Ajax
 
Making Learning Visible
Making Learning VisibleMaking Learning Visible
Making Learning Visible
 
Academia and the MOOC
Academia and the MOOCAcademia and the MOOC
Academia and the MOOC
 
Blogging As Pedagogic Practice Across the Curriculum
Blogging As Pedagogic Practice Across the CurriculumBlogging As Pedagogic Practice Across the Curriculum
Blogging As Pedagogic Practice Across the Curriculum
 
Technology Ethics: An Oxymoron?
Technology Ethics: An Oxymoron?Technology Ethics: An Oxymoron?
Technology Ethics: An Oxymoron?
 
Technical Writing Across Disciplines
Technical Writing Across DisciplinesTechnical Writing Across Disciplines
Technical Writing Across Disciplines
 
Adopting Open Textbooks
Adopting Open TextbooksAdopting Open Textbooks
Adopting Open Textbooks
 
Von Java Zu Groovy
Von Java Zu GroovyVon Java Zu Groovy
Von Java Zu Groovy
 
2013: The Beginning of the End of the University
2013: The Beginning of the End of the University2013: The Beginning of the End of the University
2013: The Beginning of the End of the University
 
Rubrics: Transparent Assessment in Support of Learning
Rubrics: Transparent Assessment in Support of LearningRubrics: Transparent Assessment in Support of Learning
Rubrics: Transparent Assessment in Support of Learning
 

Mehr Dynamik Mit Groovy

  • 1. Groovy Mehr Dynamik bei der Java- Entwicklung
  • 3. Wer ich bin... Mein eigener Chef (Extremer) Softwareentwickler (Agiler) Coach Testgetrieben http://johanneslink.net
  • 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; );
  • 21. Dynamik • Dynamische Typisierung • Closures • Meta-Programmierung
  • 22. 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;
  • 23. Closures (1) Programmlogik als vollwertiges Objekt def sorter = { unsorted -> unsorted.sort {it.name} } def sorted = sorter(people) //oder so: def sorted = sorter.call(people)
  • 24. Closures (2) • Iteration als typischer Anwendungsfall
  • 25. 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;]
  • 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
  • 35. Groovy • Volle Objekt- Orientierung ohne primitive Typen
  • 36. Groovy • Volle Objekt- Orientierung ohne primitive Typen • Optionale statische Typisierung
  • 37. Groovy • Volle Objekt- Orientierung ohne primitive Typen • Optionale statische Typisierung • Closures
  • 38. Groovy • Volle Objekt- Orientierung ohne primitive Typen • Optionale statische Typisierung • Closures • Listen und Maps als Literale
  • 39. Groovy • Volle Objekt- • Volle Integration Orientierung ohne mit der Java- primitive Typen Plattform • Optionale statische • Generics Typisierung • Annotations • Closures • Security-Model • Listen und Maps als Literale
  • 40. 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
  • 41. 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 def printSize(obj) { print obj?.size() } printSize(null) -> null
  • 42. Reguläre Ausdrücke assert (quot;Hello World!quot; =~ /Hello/) // Find-Operator assert (quot;Hello World!quot; ==~ /Hellob.*/) // Match-Operator def pattern = ~/.*ll.*/ // java.util.regex.Pattern.compile(quot;.*ll.*quot;) assert [quot;halloquot;, quot;helloquot;, quot;holaquot;].grep(pattern) == [quot;halloquot;, quot;helloquot;] def coded = quot;Hallo, JUG Cologne!quot;.replaceAll(/w+/){ match -> match.size() } assert coded == quot;5, 3 7!quot;
  • 43. Listen, ... def leer = [] def voll = [1, 2, 'JUGC'] assert voll + voll == voll * 2 assert voll[0] == 1 assert voll[0..1] == [1, 2] voll[0..1] = [0, 1, 2, 3] assert voll == [0, 1, 2, 3, 'JUGC'] assert voll[2..-1] == [2, 3, 'JUGC']
  • 44. ... Maps und Ranges def leer = [:] def voll = [a: 1, b: 2] assert voll['a'] == 1 assert voll.a == 1 voll.a = 2 assert voll == [a: 2, b: 2] def inclusive = 'a'..'z' inclusive.each {c -> print c} def exclusive = 0..<10 for (i in exclusive) {print i}
  • 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; }
  • 49. Java VM <-> Groovy
  • 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
  • 53. Dynamische Evaluation zur Laufzeit
  • 54. Dynamische Evaluation zur Laufzeit • Eval.x(users, quot;x.grep{ it.salary -> 100000 }.address.townquot;); // me, x(), xy(), xyz()
  • 55. 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)
  • 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
  • 68. Einsatzmuster für Scripting (von Dierk König) • Alleskleber • Weiches Herz • Kluge Anpassung • Endoskopische Operation • Grenzenlose Offenheit • Heinzelmännchen • Prototyp
  • 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
  • 72. 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 }
  • 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
  • 75. DSL-Beispiel (von Bernd Schiffer): Entfernungsberechnung assert 5001.m == 2000.m + 3.km + 1.m
  • 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