Mittwoch, Oktober 11, 2006

DROP COLUMN

Wenn man agil Software entwickelt, gehören häufige Produktivreleases zum Handwerkszeug. Daraus resultieren zwangsläufig Datenmigrationen, die mehr oder weniger umfangreich bei jedem Release anfallen.

Refactorings am Code verursachen entsprechende Refactorings an den Datenbankstrukturen. Besonders häufig findet man Umbenennungen von Spalten, Typänderungen von Spalten sowie das Löschen von Spalten.

Viele Datenbanken unterstützen das Umbenennen von Spalten direkt. Dort wo es nicht geht, fügt man eine neue Spalte mit dem Zielnamen ein, kopiert die Daten von der Quell- in die Zielspalte und löscht schließlich die Quellspalte. Mit ALTER TABLE und UPDATE ist das alles auch kein Problem. Bei Typänderungen von Spalten geht man genauso vor, nur dass man zwischendurch eine temporäre Spalte einführt (sonst bekommt man einen Namenskonflikt zwischen Quell- und Zielspalte).

Das Schreiben dieser Migrationsskripte scheint erstmal sehr aufwändig und unangenehm. In unseren Projekten haben wir allerdings festgestellt, dass es dann doch nicht so schlimm ist. Auf jeden Fall ziehen wir das Schreiben der Migrationsskripte dem Hinauszögern oder Weglassen von Releases vor.

Es gibt nur ein wiederkehrendes Problem und das heißt DB2: DB2 ist anscheinend die einzige Datenbank, die Tabellenspalten nicht löschen kann.

Wir behelfen uns in diesem Fall mit temporären Tabellen: Wir legen zuerst eine Kopie der Quelltabelle an und kopieren die Daten von der Quelltabelle in die temporäre Tabelle. Dann löschen wir die Quelltabelle und legen sie mit der Zielstruktur neu an. Dann migrieren wir die Daten von der temporären Tabelle in die neue Zieltabelle (mit INSERT) und löschen schließlich die Temporärtabelle. Das ist leider aufwändig und auch fehleranfällig, weil schnell mal Indices und Fremdschlüssel-Constraints verloren gehen.

Es gibt sicher gute Gründe dafür, warum DB2 DROP COLUMN nicht unterstützt - entsprechende Argumentationen findet man beim Googlen relativ schnell. Trotzdem erschwert DB2 leider agile Softwareentwicklung deutlich mehr als die anderen relationalen Datenbanksysteme.

Post bewerten

Samstag, September 23, 2006

Video: Closures in Java

Es gibt ein ganz lustiges und informatives Video, wie Closures in Java aussehen könnten und wie sie intern auf Inner Classes abgebildet werden können.

Post bewerten

Freitag, September 22, 2006

Terminplaner in Lisp

Ich habe das Terminplaner-Beispiel zuerst in Java programmiert und dann nach Groovy (Skriptsprache für die Java-Plattform) programmiert. Das Beispiel ist in Groovy als eine der jüngsten Programmiersprachen doch erheblich kürzer und einfacher als in Java. Da hat es mich dann doch gereizt, das Beispiel auch einmal in einer der ältesten Programmiersprachen zu programmieren: Lisp. Dabei habe ich Common Lisp verwendet mit CLOS (Common Lisp Object System) und Lisp-Unit von Chris Riesbeck.

Die quantitativen Ergebnisse aller drei Implementationen zuerst einmal im Vergleich:

Java

  • 5 Fachklassen, 1 Exception-Klasse, 1 Testklasse

  • 37 Methoden inkl. Konstruktoren + 10 Methoden in Testklasse

  • 246 LOC operativ + 144 LOC für Testklasse (inkl. Leerzeilen)

  • 4 For-Schleifen, 4 If-Abfragen


Groovy

  • 4 Fachklassen, 1 Exception-Klasse, 1 Testklasse

  • 30 Methoden inkl. Konstruktoren + 9 Methoden in Testklasse

  • 203 LOC operativ + 123 LOC für Testklasse (inkl. Leerzeilen)

  • Alle 4 For-Schleifen der Java-Lösung wurden durch Closures ersetzt, die 4 If-Abfragen blieben bestehen.


Common Lisp

  • 4 Fachklassen, 1 Testklasse - Exceptions werden mit Hilfe von Conditions modelliert (nur eine Zeile notwendig)

  • 17 Methoden, 1 Condition-Funktion, 10 Testmethoden

  • 76 LoC operativ, 100 LoC Test (inkl. Leerzeilen)

  • Alle 4 Schleifen der Java-Lösung wurden durch Closures ersetzt, die 4 If-Abfragen blieben bestehen, allerdings in den spezialisierten Varianten when und unless.


Wow! Die Common-Lisp-Variante hat erheblich weniger Code als die Java- oder die Groovy-Variante. Woran liegt das?
Naheliegend wäre es, die Ursache bei dem Lisp-Feature zu suchen, dass keine andere Programmiersprache anbietet: Macros (Vorsicht: Lisp-Makros sind ganz anders als die berüchtigten C-Makros). Dem ist aber nicht so. Tatsächlich habe ich in dem Lisp-Code kein eigenes Makro selbst definiert und nur an einer Stelle von der Mächtigkeit der
Lisp-Makros profitiert. Mit assert-error aus Lisp-Unit kann man sehr einfach prüfen, ob eine Exception (im Lisp-Jargon Condition) geworfen wird:


(define-test benutzer-nicht-eingeladen-exception
(setup)
(let ((termin (make-termin stefans-kalender ein-datum 180 "TDD-Dojo")))
(assert-error 'benutzer-nicht-eingeladen (lehne-termin-ab termin "Henning"))))


Zum Vergleich der entsprechende Java-Code:


public void testBenutzerNichtEingeladenException() {
Termin termin = _stefansKalender.newTermin(_jetzt, 180, "TDD-Dojo");
try {
termin.lehneTerminAb(HENNING);
fail("BenutzerNichtVorhandenException erwartet");
} catch (BenutzerNichtEingeladenException e) {
assertTrue("Exception erwartet", true);
}
}


Das erklärt aber nicht, warum der operative Code in Lisp so viel kürzer ist als in Java oder Groovy. Eine nähere Analyse fördert folgende Gründe zu Tage:
  • In Java und Groovy werden eigene Zeilen spendiert, für schließende geschweifte Klammern. Für das Lisp-Äquivalent (schließende runde Klammern) werden keine eigenen Zeilen spendiert. Dierk König schlägt in seinem kommenden Groovy-Buch diese Art der Formatierung für Groovy für bestimmte Situationen auch vor (bei den Buildern).

    • In Java und Groovy ist es üblich, Leerzeilen in Methoden einzufügen, teilweise auch zwischen Attributen. Beides macht man in Lisp eher nicht.

    • Das aufschreiben von Attributen einer Klasse ist in Common-Lisp schlanker als in Groovy oder Java. Insbesondere gibt es prägnante Abkürzungen, um Setter und Getter und Defaultwerte zu definieren.

    • Einige Implementierungen lassen sich nicht sinnvoll 1:1 nach Common-Lisp transferieren. Daher vergleicht man ein Stück weit dann doch Apfelsinen mit Orangen (so groß wie zwischen Äpfel und Birnen sind die Unterschiede dann doch nicht :-)


    Der gewaltige Unterschied in den LoC findet sich bei der Menge der Syntaxelemente in deutlich reduzierter Form. So spart man sich in Common-Lisp zwar Einiges an Zeilen für die Attributdefinitionen in Klassen, es gibt aber die gleiche Anzahl von Attributdeklarationen. In diesem Sinne ist die Lisp-Lösung zwar kürzer als die Lösungen in Java und Groovy. Sie ist bzgl. der Komplexität aber äquivalent zur Groovy-Lösung und diese beiden Lösungen sind weniger komplex als die Java-Lösung.

    Wieviel wirkt der große Unterschied in den LoC? Quelltext wird viel häufiger gelesen als geschrieben. Daher ist die Einsparung der Tastenanschläge bei der Code-Erstellung nicht wirklich von Interesse. Wenn man allerdings beim Lesen von Quelltext weder vertikal noch horizontal scrollen muss, erleichtert dies das Lesen: Im Lisp-Terminplaner passt jede operative Klasse problemlos auf eine Bildschirmseite, so dass man jede Klasse auf einen Blick erfassen kann. Das kann man als (leichten) Vorteil von Lisp werten. Allerdings muss ich zugeben, dass man den Groovy-Code an der einen oder anderen Stelle noch etwas eleganter Formulieren kann und dann auch noch etwas an LoC einsparen kann.

    Quelltext des Terminplaners in Common-Lisp.

    Post bewerten

  • Dienstag, September 19, 2006

    Refactoring und Closures

    Refactoring-schwaches Java
    Vor wenigen Tagen schrieb ich zusammen mit Henning Wolf Beispielcode für einen Sudoku-Solver. Wir wollten damit bestimmte Aspekte testgetriebener Entwicklung (TDD) sowie von Refactoring zeigen. Dabei haben wir unter anderem folgenden Code geschrieben:


    public class Sudoku {

    final static int groesse = 9;
    private Zelle[][] _sudokuArray = new Zelle[groesse][groesse];

    public Sudoku(int[][] array) {
    for (int i = 0; i < array.length; i++) {
    for (int j = 0; j < array[i].length; j++) {
    _sudokuArray[i][j] = new Zelle(array[i][j]);
    }
    }
    }

    public String[][] gibAusgefuellt() {
    String[][] result = new String[groesse][groesse];
    for (int i = 0; i < result.length; i++) {
    for (int j = 0; j < result[i].length; j++) {
    result[i][j] = _sudokuArray[i][j].toString();
    }
    }
    return result;
    }

    ...
    }



    Hier hat man offensichtlich das DRY-Prinzip verletzt (DRY = Don't Repeat Yourself): Die zwei ineinander geschachtelten FOR-Schleifen existieren zweimal (wenn man sich den kompletten Quellcode des Sudoku-Solvers ansieht, kommt das Konstrukt sogar noch häufiger vor). Ähnliche Duplizierungen in Zusammenhang mit If-Abfragen, For- und While-Schleifen finden sich in jedem größeren Java-Programm, dass ich bisher gesehen habe.

    Prinzipiell kann man diese Code-Duplizierungen auf verschiedene Arten beseitigen. Die naheliegendste Lösung besteht im Einführen eines Interfaces MatrixBesucher:


    interfaces MatrixBesucher {
    void besucheZelle (Object[][] matrix, int i, int j)
    }



    Ein Objekt dieses Interfaces reicht man als Parameter in eine Hilfemethode iteriereMatrix:


    private static void iteriereMatrix (ZellenBesucher besucher) {
    for (int i = 0; i < groesse; i++) {
    for (int j = 0; j < groesse; j++) {
    besucher.besucheZelle(i, j);
    }
    }
    }



    Dann wird aus dem Anfangs gezeigten Code:


    public class Sudoku {

    final static int groesse = 9;
    private Zelle[][] _sudokuArray = new Zelle[groesse][groesse];

    public Sudoku(final int[][] array) {
    iteriereMatrix(new ZellenBesucher() {
    public void besucheZelle (int i, int j) {
    _sudokuArray[i][j] = new Zelle(array[i][j]);
    }
    }
    }

    public String[][] gibAusgefuellt() {
    String[][] result = new String[groesse][groesse];
    iteriereMatrix(new ZellenBesucher() {
    public void besucheZelle (int i, int j) {
    result[i][j] = _sudokuArray[i][j].toString();
    }
    }
    return result;
    }

    ...
    }



    Damit ist DRY wieder hergestellt. Das Problem daran ist nur, dass sowas kein Mensch macht. Schließlich ist die Lösung länger (mit Hilfsmethode und Interface 26 statt 16 Codezeilen) und besser lesbar ist sie leider auch nicht wirklich.

    Möglicherweise schwerer wiegt, dass man für das Refactoring in Richtung DRY die Ebene wechseln musste. Das DRY-Prinzip war innerhalb einer Klasse auf Algorithmus-Ebene verletzt und wir mussten ein neues Interface einführen, also auf die Entwurfsebene wechseln. Das bedeutet, dass der Refactoringprozess an dieser Stelle nicht-linear verlaufen ist (linear: kleines Problem, kleine Lösung; nicht-linear: kleines Problem, umständliche Lösung).

    Anmerkung: Man kann sich andere Lösungen des Problems vorstellen, die ohne die umständlichen und schwer lesbaren anonymen Inner-Classes auskommen. Solche Lösungen erfordern aber deutlich mehr Entwurfsarbeit, mitunter sogar Vererbung oder den Einsatz von Entwurfsmustern. Damit würde der Refactoring-Prozess sogar noch nicht-linearer.

    Refactoring mit Closures
    Mit Closures hingegen ließe sich das DRY-Prinzip mit wenig Aufwand umsetzen und man kann beim Refactoring auf der Algorithmus-Ebene bleiben. Zu allem Überfluss ist die Lösung auch noch leicht lesbar. Hätte Java Closures, könnte die Lösung in etwa so aussehen (die Syntax für Closures ist hier an Groovy angelehnt):


    public class Sudoku {

    final static int groesse = 9;
    private Zelle[][] _sudokuArray = new Zelle[groesse][groesse];

    public Sudoku(final int[][] array) {
    iteriereMatrix({i, j -> _sudokuArray[i][j] = new Zelle(array[i][j])};
    }

    public String[][] gibAusgefuellt() {
    String[][] result = new String[groesse][groesse];
    iteriereMatrix({i, j -> result[i][j] = _sudokuArray[i][j].toString()};
    return result;
    }

    private static void iteriereMatrix (Closure closure) {
    for (int i = 0; i < groesse; i++) {
    for (int j = 0; j < groesse; j++) {
    closure.execute(i, j);
    }
    }
    }

    ...
    }



    Und schon ist die Lösung nur noch 15 Codezeilen lang (im Gegensatz zu 16 Codezeilen beim Original-Quelltext).

    Nun kann man natürlich einwenden, dass die Closures prinzipiell auch nicht mächtiger sind als anonyme Inner-Classes, es sich hier also lediglich um Syntactic-Sugar handelt. Das ist richtig und zeigt, wie wichtig eine prägnante Syntax ist. Denn die Inner-Class-Lösung wird in der Praxis so gut wie nicht eingesetzt, während die Closure-Lösung überaus üblich ist in Programmiersprachen, die Closures unterstützen wie z.B. Lisp, Python der Groovy.

    Closures in Java
    Wenn Closures so nützlich sind und konzeptionell eigentlich nur Syntactic-Sugar für anonyme Inner-Classes darstellen, dann sollte es ja eigentlich auch kein Problem sein, Closures auch in Java zu unterstützen. Und tatsächlich gibt es Anzeichen dafür, dass wir irgendwann Closures auch in Java haben werden:

    http://article.gmane.org/gmane.comp.lang.lightweight/2274

    Interessant ist hier die Argumentation: Es ist ein grundlegendes Design-Prinzip von Java, dass nur dann Objekte auf dem Heap allokiert werden, wenn der Programmierer dies explizit anfordert (typischerweise durch new). Bei Closures muss man aber implizit Speicher auf dem Heap allokieren und daher passen Closures nicht zum Design von Java.
    Nun bricht aber seit Java 5 das Auto-Boxing dieses Prinzip und daher sei es jetzt sowieso egal und man könnte dann auch noch Closures einführen :-)

    Prinzipiell bin ich persönlich der Meinung, dass Closures mir das Leben als Java-Entwickler erleichtern würden. Gleichzeitig müsste man aber große Teile des JDKs komplett umbauen. Sonst startet der ganze Ansatz als Tiger und landet als Bettvorleger. Wenn man schon Closures hat, wird man auch erwarten, dass mind. die Collections massiv davon Gebrauch machen (z.B. für das Iterieren über die Elemente). Wo man sich sonst noch die Verwendung von Closures wünschen würde, kann man gut am GDK (Groovy Development Kit) sehen.

    Und wenn man soweit geht, dann sollte man vielleicht lieber Java als Sprache auf Deprecated setzen und die Migration nach Groovy pushen. Denn da werden Closures von Beginn an unterstützt und auch entsprechende Libraries im GDK sind verfügbar.

    Post bewerten

    Montag, September 18, 2006

    Groovy und Grails auf Konferenzen

    Eindrücke aus erster Hand zu Groovy und Grails kann man sich auf verschiedenen Konferenzen in den nächsten Monaten besorgen:

    Post bewerten

    Grails: Schnell mal eine Web-Anwendung mit Groovy für die Java-Plattform

    An anderer Stelle habe ich bereits Groovy kurz beleuchtet, die standardisierte Skriptsprache für die Java-Plattform.

    Jetzt, wo man eine so mächtige und flexible Skriptsprache hat, lag es natürlich nahe, den Rails-Ansatz auch für Groovy nutzbar zu machen. Herausgekommen ist Grails.

    Grails existiert erst in der Version 0.2.2, hat aber bereits einen erstaunlichen Entwicklungsstand. Mit wenigen Clicks ist eine erste Version der Webanwendung zusammengebaut, die man dann explorativ weiterentwickeln kann.

    Die existierende Dokumentation reicht für erste Schritte aus, vieles kann man sich durch nachdenken erschließen.

    Grails ist sicherlich eine sehr interessante Technologie für die Web-Projekte, die für die Java-Plattform entwickeln müssen/wollen. Die Java-ähnliche Syntax von Groovy erleichtert Java-Entwicklern den schnellen Einstieg.

    Post bewerten

    Sonntag, September 03, 2006

    Kommentarfunktion in diesem Blog jetzt für alle

    jedenfalls für alle mit einem Blogger-Account. Bisher musste man zusätzlich Geheimwissen mitbringen: Man musste auf das # am Ende eines Eintrages klicken, um den Eintrag einzeln zu öffnen und dort konnte man dann einen Kommentar hinterlassen. Ich habe jetzt das Template für diesen Blog so geändert, dann man die Kommentarfunktion jetzt direkt am Eintrag aufrufen kann. Außerdem habe ich das kryptische # durch "Permanent Link" ersetzt.

    Dass man zum Kommentar hinterlassen einen Blogger-Account haben muss, ist ärgerlich, aber wg. der Spam-Problematik zur leider wohl unvermeidlich.

    Post bewerten

    Donnerstag, August 24, 2006

    IDEs und der K(r)ampf zwischen Statik und Dynamik

    Für Java existieren heute mit Eclipse, IntelliJ IDEA und Konsorten sehr mächtige Entwicklungsumgebungen. Cross-Referencing, Refactoring und Auto-Completion sind Features, die man nicht mehr missen möchte.
    Dass diese Features für die aktuellen Skriptsprachen a la Python, Ruby oder Groovy nicht oder nur eingeschränkt existieren, wird häufig als Argument gegen diese Skriptsprachen verwendet.

    Aber wird über diese Features wirklich reflektiert?

    Cross-Referencing
    Mit den Cross-Referencing-Möglichkeiten der IDE kann man auf Tastendruck oder Mausclick von einer Codestelle zum referenzierten Element gelangen (z.B. von einer Variablendeklaration zur Klasse, von deren Typ die Variable ist).

    Natürlich sind die Cross-Referencing-Möglichkeiten in den IDEs deutlich mächtiger als eine Textsuche über Dateien. Aber ist der Unterschied wirklich so groß? Und ist die Textsuche häufig nicht sogar schneller?

    Refactoring
    Refactoring ist eine der großen Errungenschaften in der Neuzeit der Softwareentwicklung und ihr Automatisierungsgrad ist wirklich erstaunlich hoch. Aber welche automatisierten Refactorings nutzen wir überhaupt bei der täglichen Arbeit und wie häufig? Viele Entwickler verzichten sogar absichtlich auf bestimmte einfache Automatisierungen (z.B. "Introduce Variable"), weil sie von Hand schneller sind - die Mächtigkeit der IDEs führt bei großen Projekten leider dazu, dass die Arbeit mit ihnen mitunter doch etwas zäh wird.
    Viele Refactorings kann man in der Tat mit vertretbarem Aufwand und Risiko auch von Hand durchführen. Einige andere (z.B. "Rename Class") lassen sich auch auf anderem Wege automatisieren (Find&Replace über Dateien). Natürlich kann man sich dann oft nicht 100%ig sicher sein, dass alles korrekt geändert wurde. Man muss anschließend die Tests ausführen. Dass muss man in Java aber auch, weil es immer mehr untypisierte Referenzen in XML-Dateien (Struts, Spring, EJB etc.) gibt, die den IDEs auch mal durch die Lappen gehen.

    Auto-Completion
    Bei der automatischen Vervollständigung tippt man nur den Beginn eines Wortes ein und die IDE ergänzt die fehlenden Zeichen. Dadurch spart man Tippzeit, reduziert die Wahrscheinlichkeit von Tippfehlern und muss sich nur an den Anfang von Methodennamen erinnern.
    Interessanterweise gibt es auch in programmiersprachen-unabhängigen Editoren wie Emacs oder JEdit Auto-Completion. Dabei werden viel einfachere Verfahren eingesetzt als in den Java-IDEs, die aber erstaunlich gute Ergebnisse liefern. Ein ganz einfacher Ansatz besteht z.B. darin, bei der Auto-Completion nur die Wörter zu berücksichtigen, die in der aktuellen Datei schon mal verwendet wurden. Etwas umfangreicher ist der Ansatz, die Wörter aller Dateien eines Projektes zu verwenden.
    Ähnlich wie beim Cross-Referencing sind diese Auto-Completion-Features der Editoren qualitativ denen der IDEs unterlegen. Aber wie beim Refactoring sie sind schneller.

    Und das ist ein Faktor, den man nicht unterschätzen sollte. Ich ertappe mich beim Java-Programmieren jedenfalls immer wieder dabei, dass ich Namen komplett manuell eintippe, weil ich damit schneller bin als die zäh reagierende Entwicklungsumgebung. Und wenn ich in einem einfachen Editor eine Skriptsprache programmiere, genieße ich es, dass der Editor immer sofort reagiert. Die IDEs sind nur Bruchteile von Sekunden langsamer, aber das reicht schon aus, um den Flow beim Programmieren zu beeinträchtigen.

    Post bewerten

    Samstag, August 19, 2006

    Closures in Common Lisp

    Martin Fowler published an online article about closures. He used the Ruby programming language for the examples. There are translations of the examples to Python and a language called Boo.

    As a programming task for myself I did the examples in Common Lisp, which supported Closured already decades ago. A real lisper might be able to write the code in a more elegant way but I think the principle should become clear.

    I didn't use the Common Lisp Object System (CLOS) since for this simple example an ad-hoc modeling with hashes is simpler.


    ;; Ruby
    ; def managers(emps)
    ; return emps.select {|e| e.isManager}
    ; end

    ;; Common Lisp
    (defun is-manager (emp) (getf emp :is-manager))

    (defun managers (emps)
    (remove-if-not (lambda (emp)
    (when (is-manager emp) emp))
    emps))


    ;; Ruby
    ; def highPaid(emps)
    ; threshold = 150
    ; return emps.select {|e| e.salary > threshold}
    ; end

    ;; Common Lisp
    (defun high-paid (emps)
    (let ((threshold 150))
    (remove-if-not (lambda (emp)
    (when (> (getf emp :salary) threshold) emp))
    emps)))


    ;; Ruby
    ; def paidMore(amount)
    ; return Proc.new {|e| e.salary > amount}
    ; end

    ;; Common Lisp
    (defun paid-more (amount)
    (lambda (emp) (when (> (getf emp :salary) amount) emp)))

    ;; Ruby
    ; highPaid = paidMore(150)
    ; john = Employee.new
    ; john.salary = 200
    ; print highPaid.call(john)

    ;; Common Lisp
    (let ((high-paid (paid-more 150))
    (john '(:name John :is-manager nil :salary 200)))
    (princ (funcall high-paid john)))


    ;; Tests
    (defparameter manager-list
    '((:name Stefan :is-manager nil :salary 150)
    (:name Henning :is-manager t :salary 151)
    (:name Martin :is-manager nil :salary 120)
    (:name Christian :is-manager t :salary 200)))

    (princ #\newline)
    (princ "Test function managers")
    (princ #\newline)
    (princ (managers manager-list))
    (princ #\newline)

    (princ #\newline)
    (princ "Test function high-paid")
    (princ #\newline)
    (princ (high-paid manager-list))
    (princ #\newline)

    Post bewerten

    Freitag, August 04, 2006

    Sensing Variables for Refactorings

    Michael Feathers hat ein Konzept namens Sensing Variables beschrieben, mit dem sich Refactorings an Methoden sicherer durchführen lassen. Ein häufiges Problem mit dem Refactoring "Extract Method" besteht darin, dass der selektierte Code-Block sich nicht automatisch in eine Methode verschieben lässt (z.B. weil die neue Methode dann mehrere Rückgabewerte haben müsste). Also muss man erstmal manuell die Methode manipulieren, bevor man "Extract Method" ausführen kann. Dazu muss man häufig die Reihenfolge von Anweisungen in der Methode ändern, kann aber nur schwer feststellen, ob sich durch das Verschieben die Semantik der Methode ändert. Sensing Variables helfen, an dieser Stelle mehr Sicherheit zu bekommen.

    Post bewerten

    DSL: Domain Specific Languages

    Martin Fowler hat einen kurzen Artikel über DSLs in seinem Blog veröffentlicht. In dem Artikel erklärt er den Unterschied zwischen General Purpose Languages (GPL) und DSLs. Außerdem argumentiert Fowler, dass der Übergang zwischen APIs und DSLs fließend ist. Das API einer Bibiothek könne durchaus vergleichbar sein mit einer DSL.

    Das finde ich sehr überzeugend, vor allem wenn man sich die dynamischen Skriptsprachen a la Ruby, Groovy oder Lisp ansieht. Dort wo in Java eigene DSLs für Build-Skripte (ANT), Datenbankabfragen (Hibernate Query Language, HQL) oder Oberflächenbeschreibung (HTML, JSP) verwendet werden, haben Ruby und Konsorten einfach nur Bibiotheken, die innerhalb der Programmiersprache verwendet werden.

    Interessant finde ich, dass alle Artikel zum Thema DSL, die ich kenne (ich gebe zu, dass das nicht allzu viele sind), sich ausschließlich um Technologie-Domänen kümmern. Es kommen immer wieder dieselben technischen Beispiele für DSLs: SQL, ANT, etc. Das finde ich schade. Schließlich sind die technischen Aspekte in einem Softwareprojekt eher uninteressant im Gegensatz zu den fachlichen. DSLs nur für technische Aufgaben zu verwenden, riecht nach einer Optimierung an der falschen Stelle (wenn ich in einem Bereich 50% Aufwände spare, der nur wenige Prozent meiner Gesamtaufwände ausmacht, ist der Gesamtgewinn marginal).

    Eigentlich müssten wir nach fachlichen DSLs suchen. Interessiert das Niemanden, ist das so schwer oder lese ich einfach nur die falschen Quellen?

    Post bewerten

    Freitag, Juli 28, 2006

    Flow - Softwareentwicklung im Fluss

    Wenn ich heutige Java-Projekte sehe, dann ertappe ich mich immer wieder bei derselben Frage: "Muss das alles so schwerfällig sein?" Um dieser Frage nachzugehen, habe ich mich selbst gefragt, wie ich mir Softwareentwicklung vorstelle. Dazu fällt mir zuerst ein Wort ein: Flow

    Meine Gedanken dazu habe ich in einem Online-Artikel niedergeschrieben.

    Post bewerten

    Sonntag, Juli 23, 2006

    YouOS: Bye, bye Betriebssystem?

    YouOS ist ein webbasiertes Betriebssystem mit allerhand Anwendungen. Das ganze Ding ist noch im Alpha-Stadium, aber die Richtung ist schon mal interessant:

    • Keine Installation von Software auf dem eigenen Rechner.
    • Zugang zu den eigenen Programmen und Daten von jedem Rechner der Welt aus.
    • Automatische Datensicherung
    • Zentrale Sicherheitsmechanismen gegen Viren und andere Angriffe

    Eigentlich ist das Ganze eine naheliegende Idee. Mit Writeboards, Writely, GMail, NumSum, Flickr und anderen sind längst mehr Funktionen im Internet verfügbar, als der durchschnittliche Anwender benötigt. Was bisher fehlte, war die Integration. Man musste mehrere Accounts verwalten, sich URLs merken und konnte schlecht übergreifend über die Systeme arbeiten.

    YouOS leistet das noch nicht vollständig, aber das Potenzial ist da.

    Post bewerten

    Dienstag, Juli 18, 2006

    Buch: Hackers and Painters

    Ich habe gerade das Buch "Hackers and Painters" von Paul Graham durchgelesen. In dem Buch ist eine Reihe von Essays veröffentlicht, die (fast?) alle auch online auf auf der Web-Site von Paul Graham verfügbar sind. Trotzdem lohnt sich das Buch, um es in der Bahn oder im Urlaub zu lesen.

    Die Essays decken verschiedene Themenbereiche ab begonnen vom typischen Hacker-Charakter über Innovationen und Startups in der IT bis hin zum Programmiersprachenentwurf. Dabei vertritt Paul Graham häufig ungewöhnliche Ansichten und Ideen und regt damit zum Denken außerhalb der gängigen Bahnen an (warum die nächste Webanwendung nicht in Lisp schreiben?).

    Mir hat das Buch jedenfalls sehr gut gefallen.

    Post bewerten

    Dienstag, Juli 11, 2006

    Verantwortung in der Softwareentwicklung

    Ich habe zusammen mit Henning Wolf einen Artikel über Berufsethos und Verantwortung in der Softwareentwicklung im Java-Magazin veröffentlicht. Der Artikel ist online verfügbar.

    Der Artikel ist kontroverser als der Titel vielleicht vermuten lässt. Die drei Hauptthesen des Artikels sind:
    1. Jeder ist verantwortlich für alles, was er tut.
    2. Jeder ist verantwortlich für alles, was er denkt.
    3. Jeder ist verantwortlich für alles, was mit ihm geschieht.

    Wer würde das schon einfach so unterschreiben? Wer nicht, sollte den Artikel lesen.

    Post bewerten

    Mittwoch, Juli 05, 2006

    Regeln vs. Eigenverantwortlichkeit in agilen Projekten

    Bei den agilen Methoden gibt es deutliche Unterschiede in den Vorgaben und Regeln, die sie definieren. So ist z.B. Feature-Driven-Development sehr strikt bzgl. Rollen und Verantwortlichkeiten und eXtreme Programming definiert den Entwicklungsprozess sehr detailliert. Auf der anderen Seiten steht Scrum, dass nur grob regelt, wie die Anforderungen in das Projekt eingebracht werden. Die restliche Ausgestaltung des Projektes obliegt den Entwicklern.

    Was ist das bessere Modell? Wieviel Freiheit brauchen die Entwickler, um höchste Produktivität zu erreichen und wieviel Regeln muss man definieren, damit das Projekt nicht im Chaos versinkt?

    Vielleicht hilft bei der Beantwortung dieser Frage das Dreifus-Modell der Qualifikationsaneignung (Skill Acquisition). Demnach brauchen Anfänger Regeln und Vorgaben. Bei der Arbeit in dem so vorgegebenen Rahmen entwickelt sich Erfahrung und Intuition, so dass die Regeln und Vorgaben immer weniger wichtig werden und sogar behindern können. Also wären Feature-Driven-Development und eXtreme Programming eher für Neulinge geeignet und Scrum für erfahrene Entwickler?

    Post bewerten

    Dienstag, Juni 20, 2006

    XP-Days 2006 in Hamburg


    In diesem Jahr finden die XP-Days in Hamburg statt. Sie werden dieses Jahr zusammen von Andrena und it-agile ausgerichtet. Nähere Infos zum Einreichen von Beiträgen gibt es unter http://www.xpdays.de.

    Post bewerten

    Samstag, Juni 17, 2006

    Korrektur zu GroUnit: JUnit ohne Bytecode

    Dierk König wies mich darauf hin, dass mein erstes Argument für GroUnit im Gegensatz zu JUnit so nicht stimmt . Mit groovy.util.AllTestSuite kann man sehr wohl in JUnit auch direkt Groovy-Quellcode testen, ohne diesen vorher nach Bytecode zu compilieren.
    Vielen Dank an Dierk für die Klarstellung.

    Post bewerten

    Sonntag, Juni 11, 2006

    GroUnit: Unittests mit Groovy


    Ursprünglich als reine Fingerübung gedacht, habe ich ein Unittest-Tool für Groovy geschrieben: GroUnit. Das ist nicht weiter schwer, weil Groovy bereits Unterstützung für Unit-Tests mitbringt, allerdings ohne UI. Man kann wg. der nahtlosen Groovy-Java-Integration JUnit auch für Groovy verwenden (siehe z.B. Groovy-Web). Das hat aber ein paar Nachteile:

    • Man muss die Groovy-Klassen nach Bytecode compilieren. Für Produktionscode ist das OK, aber während der Entwicklung verlängert es unnötig die Turn-Around-Zeiten.

    • Wenn ein Test fehlschlägt, bekommt man im Stack-Trace auch die ganzen Core-Reflection-Geschichten angezeigt, die Groovy im Hintergrund durchführt. Dadurch muss man immer erst mal eine Weile im Stacktrace suchen, bis man das eigentliche Problem überhaupt gefunden hat.

    • Seit JUnit 4.0 bringt JUnit keine eigene Benutzungsoberfläche mehr mit. Diese wird von den Entwicklungsumgebungen gestellt. Das ist für Jave wunderbar. Es erzwingt für Groovy aber, dass man eine schwergewichtige Java-Entwicklungsumgebung verwendet. Häufig wäre man mit einem mächtigen Editor besser bedient.


    Ich habe versucht, in GroUnit diese Probleme zu adressieren. Sehr weit bin ich in der ersten Version 0.1 sicher noch nicht gekommen. Aber vielleicht engagieren sich noch weitere Leute für GroUnit und es wird noch was richtig Gutes.
    Zur GroUnit-Homepage mit Download-Möglichkeit.

    Post bewerten

    Freitag, Juni 09, 2006

    Groovy im Vergleich zu Java am Terminplaner-Beispiel

    Ich habe den Terminplaner, den ich als Beispiel für testgetriebene Entwicklung programmiert hatte, von Java auf Groovy migriert. Für den Terminplaner in Java hatte ich testgetrieben ca. 180 Minuten benötigt. Für die Migration nach Groovy habe ich dann ca. 60 Minuten investiert.

    Der erste Unterschied der Groovy-Lösung zur Java-Lösung ist der Wegfall der Klasse TeilnahmeStatus (war Enum bei Java). Die Teilnahme-Status-Informationen landen bei Groovy einfach in der Klasse Teilnahme.

    Die equals- und compareTo-Methoden sind in Groovy einfacher zu implementieren, weil es für beides eigene Operatoren (== und <=>) gibt, die mit Null-Werten umgehen können.

    Bei einer der compareTo-Methoden habe ich bei der Migrationen einen Fehler gefunden, der im Groovy-Code sofort offensichtlich wird. Im Java-Code kann man den Fehler durch die Klammerung beim flüchtigen Lesen übersehen (ist mir beim Schreiben der Java-Lösung ja offensichtlich auch passiert).

    Die betroffene Java-Zeile sieht so aus:

    int startZeitCompare = startZeit.compareTo(startZeit);

    In Groovy wird der Problem sofort offensichtlich:

    int startZeitCompare = startZeit <=> startZeit

    Es muss natürlich heißen:

    int startZeitCompare = startZeit <=> o.startZeit

    Die Zahlen nach der Umstellung lesen sich so:

    Java-Zahlen
    • 5 Fachklassen, 1 Exception-Klasse, 1 Testklasse
    • 37 Methoden inkl. Konstruktoren + 10 Methoden in Testklasse
    • 246 LOC operativ + 144 LOC für Testklasse (inkl. Leerzeilen)
    • 4 For-Schleifen, 4-If-Abfragen


    Groovy-Zahlen
    • 4 Fachklassen, 1 Exception-Klasse, 1 Testklasse
    • 30 Methoden inkl. Konstruktoren + 9 Methoden in Testklasse
    • 203 LOC operativ + 123 LOC für Testklasse (inkl. Leerzeilen)
    • Alle 4 For-Schleifen wurden durch Closures ersetzt, die 4-If-Abfragen blieben bestehen.


    Die Code-Ersparnis lässt sich im Wesentlichen auf folgende Groovy-Konzepte zurückführen:
    • Closures machen For-Schleifen überflüssig und sind kürzer aufzuschreiben.
    • Durch Properties werden Getter und Setter überflüssig.
    • Typecasts entfallen.
    • In den Tests spart die Uniformität von Arrays und Collections Konvertierungen.


    In Groovy habe ich also ca. 80% der LOC (Lines of Code) benötigt, die ich für die Java-Lösung benötigte. Das hört sich erstmal nicht nach einem Quantensprung an. Allerdings muss man bedenken, dass ich „lediglich“ Java durch das doch sehr ähnliche Groovy ersetzt habe. Also hat eine Einzelmaßnahme 20% Code-Ersparnis gebracht. Setzt man das einfach mal naiv mit 20% Kostenersparnis gleich, ist man sehr schnell in Regionen, die interessant erscheinen (die meisten Offshoring-Untersuchungen sprechen von geringeren Einsparungspotenzialen).

    Außerdem ist zu bedenken, dass mein Terminplaner-Beispiel die Strukturen in den Vordergrund stellt und die Daten ignoriert (so hat der Benutzer nur seinen Namen und der Termin nur 4 Datenfelder). In einer vollständigen Terminplaner-Anwendung sind viel mehr Datenfelder zu verwalten (MS-Outlook hat für Kontakte so um die 30 Datenfelder). Und gerade wenn es nur um das Halten von Datenfeldern geht, spielt Groovy seinen LoC-Vorteil aus. Die 30 Datenfelder würden in Groovy ca. 10 LoC bedeuten und in Java ca. 130 LoC (beide Zahlen inkl. Leerzeilen).

    Nicht zuletzt habe ich die Terminplaner-Anwendung relativ stumpf auf Groovy umgestellt. Ich muss prüfen, ob sich der Code durch fortgeschrittene Groovy-Konzepte noch weiter vereinfachen lässt.

    Der Groovy-Code findet sich hier zum Download.

    Post bewerten