Posts mit dem Label programming werden angezeigt. Alle Posts anzeigen
Posts mit dem Label programming werden angezeigt. Alle Posts anzeigen

Mittwoch, 25. Juni 2014

Test Driven Development - Some Practical Insights

Für mich wirken Softwareprojekte, in denen nicht testgetrieben entwickelt wird, wie Artefakte aus der Steinzeit. Erinnert ihr euch an die Zeit, in der es noch keine Gurtpflicht im Auto gab? Kaum zu glauben, oder? Ich fühle mich ohne TDD etwa genauso wie heute ohne angelegten Gurt.


Am Dienstag, den 24. Juni 2014, durfte ich einen Vortrag über meine Erfahrungen mit Test Driven Development (TDD) halten. Summa-Summarum habe ich dort eine Lanze für TDD gebrochen.
 
Meine Kernbehauptung war, dass TTD die "Silver Bullet" des Softwareengineering sein kann, sofern man richtig "zielen" kann und die passenden "Waffen" einsetzt. Man sollte verstehen, wie TDD funktioniert und mit welchen Mitteln das am besten geht.
Wenn's euch interessiert, hier sind die Folien.

https://www.dropbox.com/s/k1tlskfq3tvfkr2/TDD-final.pdf

Interessant fand ich auch, dass von zwanzig Teilnehmern grade mal zwei an ihrem Arbeitsplatz TDD einsetzen. Immerhin gibt es das Thema schon einige Zeit (aber ihr werdet kaum glauben, wie alt TDD ist). Wäre interessant mal zu untersuchen, woran das liegt. Vielleicht sind die Gründe ähnlich den unsrigen - wir wussten einfach nicht, wie man TDD richtig macht. Oder es liegt vielleicht daran, dass die Entscheider keinen Mehrwert erkennen können. Naja, wer das Problem hat, kann sich ja mal meine Folien ansehen, da ist der Mehrwert wohl kaum zu übersehen.



 
Der Vortrag hat im Rahmen des Arbeitskreises "Software Engineering Live" der GI/GChACM-Regionalgruppe München stattgefunden. Hier ist der Teaser dazu:

Thema: Praxiserfahrungen mit Test Driven Development
Referent: Michael Karneim, Sanacorp GmbH 

In diesem Vortrag erzählt der Softwarearchitekt Michael Karneim von der Einführung von Test Driven Development (TDD) bei einem Pharmagroßhandelsunternehmen. Er berichtet von den Erkenntnissen, die er während dieses Projekts gewonnen hat. Er gibt einen Überblick über die Techniken, die seiner Meinung nach essentiell für die erfolgreiche Einführung sind: Gute Testframeworks, Akzeptanztests, Test-DSLs, Test-Data-Factories, Backdoors und Continuous Integration. Er hat die Erfahrung gemacht, dass deren Einsatzaufwand sich schon in der Anfangsphase eines Projekts amortisieren kann. Insbesondere spricht er von den Auswirkungen, die TDD auf die Entwicklungsgeschwindigkeit, die Softwarearchitektur und Aspekte der Softwarequalität jenseits der Korrektheit hat. Seines Erachtens kann der positive Einfluss von TDD gar nicht hoch genug bewertet werden. Dennoch ist TDD keine Silver Bullet. Der Erfolg in einem Projekt ist neben der Auswahl der richtigen Techniken insbesondere davon abhängig, dass das Team diesen Ansatz verinnerlicht hat und sein Hauptaugenmerk nicht mehr allein auf den Code, sondern zu einem wesentlichen Teil auf die Tests legt.

Freitag, 27. September 2013

Video zum Beanfabrics-Vortrag

Im Juni 2013 hatte ich das Vergnügen einen kleinen Vortrag vor den Mitarbeitern der TNG halten zu dürfen. Das Thema war Beanfabrics, ein Software-Projekt, dass ich seit 1999 voran treibe. Freundlicherweise hat Gerhard Müller mir gestattet, einen Link auf das Video zu veröffentlichen.


Hier ist noch ein Link auf die Vortragsfolien.

Sonntag, 2. Dezember 2012

Runtime Generic Type Resolution for Java

Bekanntlich stehen in Java die meisten generische Typinformationen zur Laufzeit nicht zur Verfügung. Insbesondere können sie nicht mittels Reflection ausgelesen werden. Doch stimmt diese Aussage unter allen Umständen?

Das Verfahren, bei dem die Typinformationen gelöscht werden, heisst Type Erasure. Damit soll das sogenannte "Generic Legacy Problem" gelöst werden.

Aus Gründen der Rückwärtskompatibilität mit altem Code, hat man sich damals entschieden, beim Kompiliervorgang die Generics weitgehend zu löschen oder in andere Konstrukte zu transformieren. Im Detail löscht der Kompiler die Typparameter, ersetzt die Typrariablen durch ihren Grenztyp, fügt Typecasts hinzu, und erweitert Klassen um sogenannte Brückenmethoden, damit das Overriding auch weiterhin funktioniert.

Folglich kann zur Laufzeit nicht herausgefunden werden, welchen Typ die unten stehende Liste "myList" hat:

public class Scribble { 

    public static void main(String[] args) {
        List<String> myList = new ArrayList<String>();

        Class elementType = getElementTypeOf(myList);
        System.out.println(elementType);
    }

    /**
    * Returns the generic element type of the given list.
    */
    public static Class getElementTypeOf(List list) {
        // This is not possible in Java !?!
    }
}

Für das obige Beispiel ist das tatsächlich unmöglich. Allerdings ist das nicht die ganze Wahrheit. Wenn wir den Code etwas abwandeln, dann lässt sich die Typinformation auslesen:

public class Scribble { 

    static List<String> myList = new ArrayList<String>();

    public static void main(String[] args) {
        Class elementType = getElementTypeOf("myList");
        System.out.println(elementType);
    }

    /**
    * Returns the generic element type of the field with the given name
    */
    public static Class getElementTypeOf(String fieldname) {
        return new GenericType(Scribble.class)
                   .getFieldType( fieldname)
                   .getTypeParameter(List.class.getTypeParameters()[0])
                   .asClass();
    }
}

Das liegt daran, dass die Variable "myList" nun als Member der Klasse deklariert ist, d.h. mit vollständigen Metainformationen im Classfile abgelegt wird.

Die Klasse "GenericType" kapselt die Suche nach dem Wert des Typparameters, welche mit Hilfe der etwas umständlichen Reflection API realisiert ist. Sie ist Teil des gleichnamigen Github-Projekt GenericType.

Dienstag, 1. November 2011

Ein Codegenerator für POJO-Builders


Seit Ende September beschäftige ich mich auf Github mit einem Open-Source-Projekt mit dem Namen PojoBuilder.

Ich entwickle dort einen Java-Codegenerator, der für ein beliebiges POJO einen passenden Pojo-Builder erzeugen kann. Dieser bietet die folgende Funktionalitäten:

  • ein "fluent interface" zum Festlegen der Eigenschaftswerte des POJOs
  • und eine build-Methode für die Erstellung einer neuen POJO Instanz mit diesen Werten
Unter anderem eignet sich so ein Builder besonders für die Erstellung von Testdaten im Rahmen von automatisierten Tests, wie das Nat Pryce anschaulich beschrieben hat.

Ein kleines Beispiel

Der Codegenerator lässt sich naheliegenderweise am einfachsten anhand eines Code-Beispiels erläutern. Nehmen wir an, wir haben die folgende Javaklasse erstellt und benötigen dafür einen Builder.
public class Contact { 
    private final String name;
    private String email;

    public Contact(String aName) {
        this.name = aName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }
}

Der dazugehörige Builder sollte beispielsweise auf die folgende Weise bedient werden können:

Contact james = new ContactBuilder()
    .withName("James Bond")
    .withEmail("007@secretservice.org")
    .build();

Mit dem Einsatz von PojoBuilder lässt sich dieses Ziel durch die Markierung der Klasse mit zwei Annotations erreichen.

@GeneratePojoBuilder(intoPackage = "samples.builder")
public class Contact { 
    private final String name;
    private String email;

    @ConstructorProperties({ "name"})
    public Contact(String aName) {
        this.name = aName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }
}

Mit @GeneratePojoBuilder wird der Codegenerator angewiesen, für die annotierte Klasse einen Builder zu generieren. Das Attribut intoPackage ist optional und bestimmt das Java-Package, in das der Builder generiert werden soll. Mit @ConstructorProperties legt man fest, welcher Konstruktor verwendet werden soll und wie die Parameter auf Properties abgebildet werden.

Der generierte Builder sieht dann so aus (Kommentare habe ich entfernt):

public class ContactBuilder implements Cloneable {

   protected String value$email$java$lang$String; 
   protected boolean isSet$email$java$lang$String = false; 

   protected String value$name$java$lang$String; 
   protected boolean isSet$name$java$lang$String = false; 

   public ContactBuilder() {
   }

   public ContactBuilder withEmail( String value) {
      this.value$email$java$lang$String = value;
      this.isSet$email$java$lang$String = true;
      return this;
   }

   public ContactBuilder withName( String value) {
      this.value$name$java$lang$String = value;
      this.isSet$name$java$lang$String = true;
      return this;
   }

   public Object clone() {
      try {
            ContactBuilder result = (ContactBuilder)super.clone();
            return result;
      } catch (CloneNotSupportedException e) {
            throw new InternalError(e.getMessage());
      }        
   }
   
   public ContactBuilder but() {
      return (ContactBuilder)clone();
   }

   public Contact build() {
      Contact result = new Contact( this.value$name$java$lang$String );
      if ( this.isSet$email$java$lang$String) {    
           result.setEmail( this.value$email$java$lang$String);  
      }
      return result;
   }
}

Wie ist das ganze nun implementiert?

Für die eigentliche Codegenerierung nutze ich, wie schon in früheren Projekten, die Stringtemplate Engine. Offen war ursprünglich lediglich die Frage, wie der Generator angestoßen werden sollte. Ich wollte erreichen, dass er sowohl in einem Build-Lauf als auch in einer IDE genutzt werden kann. Da sie sich für beides sehr gut eignet, habe ich mich für die Annotation Processor API entschieden, die seit Java 5 verfügbar ist. Somit war auch festgelegt, dass der Generator über Annotations gesteuert wird (und nicht etwa über XML oder eine andere Beschreibungssprache).

Automatisierte Tests

Eine Schwierigkeit im Projekt ist allerdings die Frage, wie man einen Annotation-Processor ordentlich testet, und zwar im Rahmen von Acceptance-Tests sowie Unit-Tests (ja, ich gebe zu, die Tests nicht vorher geschrieben zu haben). Bislang meine ich immerhin einen guten Weg für die Acceptance-Tests gefunden zu haben, und zwar mittels einer JavaProject-Fassade für die Java Compiler API, mit der ich tatsächlich on-the-fly ein kleines Java-Projekt komplett mit Sourcen erstellen und kompilieren kann. Anschließend kann ich in meinem TestCase die generierten Klassen per Classloader laden und mittels Reflection prüfen, oder alternativ dazu, könnte ich auch vorbereitete Unit-Tests laufen lassen, welche die generierten Builder testen.

Was allerdings das Unit-Testen des AnnotationProcessor angeht, tappe ich noch im Dunkeln. Grundsätzlich bin ich ein ein Fan von Interaction-Based-Testing, allerdings erscheint mir in diesem Fall mit Mock-Objekten zu arbeiten sehr aufwändig. Alternativ dazu bleibt wohl nur ein State-Based Ansatz, wie etwa in diesem Post auf Stackoverflow beschrieben.

Dienstag, 9. August 2011

Tower Deconstruction in 3D

Wie im letzten Post erwähnt, beschäftige ich mich gelegentlich und sehr Hobby-mäßig mit Spieleentwicklung. Aktuell versuche ich mein Glück mit Unity3d.

Nun habe ich ein kleines aber vollständiges Spiel online gestellt, komplett mit serverseitiger Highscore-Liste.

Towers ist ein kleines Casual Game für alle, die gerne Türme einreißen. Ziel ist es, mit nur 10 Feuerbällen möglichst viele Bauklötze vom Tisch zu schubsen.

Steuerung
"Cursor rechts" & "Cursor links: Kamera bewegen
Maus bewegen: Zielen
Linker Mausknopf: Feuern



Samstag, 30. Juli 2011

Do-It-Yourself Spieleentwicklung mit Unity 3D

Habe seit langem mal wieder eine aktuelle Version von Unity 3D runter geladen und damit herum gespielt. Und ich muss sagen, ich bin beeindruckt.

Wer JavaScript programmieren und Google SketchUp bedienen kann, dem steht die Laufbahn als Spiele-Entwickler vermutlich so offen wie nie zuvor. Die 3D-Objekte (Meshes) kann man mit SketchUp selbst entwerfen oder ganz einfach im Google's 3D-Warehouse besorgen. Anschliessend exportiert man das Ganze als FBX-Datei. Per Drag & Drop fügt man die Datei als Asset in das Unity-Projekt ein. Nun noch ein paar Geräusche von einer alten FX-Sound-DVD kopieren und los geht's.

Ein Unity-Projekt besteht aus einem Assets-Ordner (mit Bildern, Sounds, etc.) und einer Liste von Scenes. Eine Scene besteht aus einem Baum von GameObjects. Jedes GameObject kann mit beliebig vielen Components ausstaffiert werden, die dessen Aussehen und das Verhalten bestimmen. Beispiele sind Light, Mesh, Collider, Rigidbody, AudioSource und Script.

Hat man alles zusammen gesteckt, so klickt man oben auf den Play-Button und kann das Spiel ausprobieren. Ausserdem kann man Binaries für Mac, Windows, Xbox, PS3, Wii, Android und iPhone generieren. Da bleiben keine Wünsche offen...

Deep Sea Mining

Unten seht ihr ein paar Screenshots aus dem Spiel "Deep Sea Mining", das ich gestern schnell zusammen gezimmert habe. Wer es ausprobieren will, kann das Programm für Windows und Mac aus meiner Drop Box holen. Gesteuert wird mit den Cursor-Tasten und der Leerttaste. Viel Spass!

 

 

 

Montag, 28. Februar 2011

Was ist eine funktionale Programmiersprache ohne Listen?


Oder anders ausgedrückt: wie könnte man sich einer funktionalen Sprache besser nähern, als über Listen? Auf www.simplyscala.com habe ich die folgenden Beispiele durchgespielt.

Der graue Text sind meine Eingaben. Der grüne Text sind die Ausgaben des Interpreters.

var la = List(1,4,6,9,10,20,11,3)
la: List[Int] = List(1, 4, 6, 9, 10, 20, 11, 3)

Mit List(x, y, ...) kann ich offenbar eine Liste von Zahlen definieren. Und la=... weist das neue Objekt der Variable la zu.

var lb = la ++ List(-3,-2,-8)
lb: List[Int] = List(1, 4, 6, 9, 10, 20, 11, 3, -3, -2, -8)

Mittels ++ kann man also Listen aneinander hängen.

var lc = lb.sorted
lc: List[Int] = List(-8, -3, -2, 1, 3, 4, 6, 9, 10, 11, 20)

Die Funktion sorted ist eine Methode der Liste und liefert, nicht unerwartet, eine sortierte Variante.

var ld = lc.filter( _ < 0)
ld: List[Int] = List(-8, -3, -2) 

Aha, filtern kann man also auch. Der Ausdruck zwischen den runden Klammern ist das Prädikat, anhand dessen die Liste gefiltert wird. Es wird auf jedes Element der Liste angewendet. Wenn es zu true evaluiert, dann wird das Element (dargestellt durch ein "_") in die neue Liste aufgenommen, andernfalls wird es verworfen.
Vermutlich kann ich dafür auch eine Funktion verwenden?!

def isNeg(v:Int) = v < 0
isNeg: (v: Int)Boolean 
 
Wie man sieht, kann man mit def ... = ... in Scala eine Funktion definieren.
Verwenden wir die doch gleich mal.

var le = lc.filter(isNeg(_))
le: List[Int] = List(-8, -3, -2) 
 
Ok. Das hat geklappt.
Ob man auch zwei Listen auf einmal erzeugen kann?

var (low, hi) = lc.partition(isNeg(_))
low: List[Int] = List(-8, -3, -2)
hi: List[Int] = List(1, 3, 4, 6, 9, 10, 11, 20)

Die Methode partition zerlegt die Liste anhand des Prädikats in zwei Listen ("Die Guten ins Töpfchen, die schlechten ins ..."). Der Rückgabewert ist ein Tuple. Hier habe ich es gleich wieder in seine Bestandteile zerlegt und den beiden Variablen zugewiesen.

Zu guter Letzt' wollen wir doch mal eine Funktion in Action sehen...

def f(x:Int) = 2*x+3
f: (x: Int)Int

Das kenn' ich noch aus der Schule: "F von x ist 2 mal x plus 3" :-)
Und nun mal anwenden lassen...

var y = List(-3,-2,-1,-0,1,2,3).map( f)
y: List[Int] = List(-3, -1, 1, 3, 5, 7, 9)

Die Methode map wendet die angegebene Funktion auf jedes Listenelement an und stopft das Ergebnis in die Ergebnisliste.

Das war's für heute.

M.

Sonntag, 12. Juli 2009

Aktivitätsanzeige im iPhone nutzen

In der oberen Statuszeile des iPhones ist eine Aktivitätsanzeige versteckt.






Mit dem folgenden Code kann man sie nutzen:

UIApplication* app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;
// start loading data ...
// ...
// finished.
app.networkActivityIndicatorVisible = NO;

Montag, 1. Juni 2009

Spielerei mit JavaFX


Hier ist ein Link auf ein kleines Spiel, das ich mit JavaFX 1.1 implementiert habe.

Der Wassertropfen kann mit den Cursortasten gesteuert werden, oder alternativ mit der Maus.
Das Ziel jedes Levels ist es, vor Erreichen des Ausgangs möglichst viele Blumen zu gießen.

Viel Spaß,

- Michael