Follow my new blog

Donnerstag, 31. Dezember 2009

Linksammlung zum Thema Monads [OOP 2010]

image Bei der Beschäftigung mit Funktionaler Programmierung stoße ich immer wieder auf den Begriff Monad. Leider bringt mich jedoch der zugehörige Wikipedia-Artikel bei dessen Verständnis nicht weiter. Deshalb habe ich jetzt ein wenig gegooglet. Für mich als Entwickler, der bisher (vor allem) mit imperativen Programmiersprachen zu tun hatte, sind dabei die folgenden Beiträge herausgekommen, die ich als verständnisfördernd empfinde.
So ganz bin ich mit diesen Beiträgen allerdings immer noch nicht zufrieden. Ich finde die Erklärungen für Monads immer noch recht theoretisch und fern der imperativen objektorientierten Programmierpraxis.

Wie würde ich nun Monads erklären? Hm... mal sehen. In einem zukünftigen Beitrag versuche ich das mal.

Samstag, 26. Dezember 2009

Neues Format für längere Blogartikel? [OOP 2010]

Es gibt keine formale Beschränkung für die Länge von Blogartikeln. Und so habe ich bisher kürzere wie längere unterschiedslos hier gepostet. Nun mir jedoch Zweifel gekommen, ob ich das auch weiterhin tun soll.

Online Zeitungen verfahren ja schon lange so, dass sie längere Beiträge splitten. Beispiel www.zeit.de: Artikel werden zunächst häppchenweise gezeigt, wie Sie hier sehen können; erst wenn Sie “Auf einer Seite lesen” anklicken, bekommen Sie alles auf einer Seite. Die Zeit hat für solche verschiedenen “Views” natürlich ein hübsches Redaktionssystem, bei dem auch noch ein PDF rausfällt. Das kann ich mir natürlich nicht leisten. Was also tun?

Wie wäre es denn, wenn ich längere Beiträge nicht mehr direkt ins Blog schriebe, sondern wie folgt veröffentlichen würde:

Zustand als Abhängigkeit - IoC konsequent gedacht

Wenn Sie mögen, können Sie diese Ansicht auf den ganzen Bildschirm vergrößern. Oder Sie stellen vom “Book View” um auf den “Scroll View”, um quasi wieder alles auf einer langen Seite zu haben. Oder Sie drucken den Inhalt aus. Oder Sie exportieren ihn als PDF, um ihn auf Ihrem favorisierten ebook-Reader zu lesen.

Was meinen Sie? Wäre solche Differenzierung in der Publikation nicht eine gute Sache? Blogartikel, die ca. eine Bildschirmseite lang sind, veröffentliche ich weiterhin direkt im Blog. Aber Artikel von mehreren Bildschirmseiten oder gar mehreren Druckseiten biete ich Ihnen im obigen Format.

Denn seien wir ehrlich: Längere Texte am Bildschirm zu lesen, ist immer unschön. Es macht wenig Unterschied, ob sie häppchenweise präsentiert werden oder in einem Stück. Längere Inhalte brauchen Überblick. Den bietet nur ein Ausdruck – wofür ein Blog keine wirklich gute Grundlage ist – oder eine Ganzseitendarstellung im Hochformat auf einem speziellen Reader.

Vergleichen Sie einmal den Text im obigen Viewer bzw. nach Ausdruck mit der ursprünglichen Veröffentlichung. Welche Präsentation finden Sie lese- bzw. verständnisfreundlicher?

Nachtrag: In einigen Kommentaren zu diesem Posting wurde gegen solche Flash-Darstellung eingewandt, dass der Content dann nicht von Suchmaschinen indiziert würde. Das ist jedoch falsch wie diese Suche beweist: http://www.google.de/search?q="zustand+als+abhängigkeit"+site:scribd.com (die Einschränkung mit site:scribd.com habe ich nur gemacht, um den Link zum Dokument auf die erste Ergebnisseite zu bringen). Das erste Ergebnis ist ein Link auf das obige Dokument: http://www.scribd.com/doc/24512457/Zustand-als-Abhangigkeit-IoC-konsequent-gedacht.

Objektorientierung als Behaviorismus der Softwareentwicklung [OOP 2010]

Die Objektorientierung steht uns im Weg zu evolvierbareren Programmen. Ja, das glaube ich immer mehr. Nicht, dass ich Objekte missen möchte – sie sind nützliche und wichtige Strukturierungsmittel für Software.  Ich will natürlich Funktionseinheiten mit eigenem Zustand definieren können. Aber die Sprache der Objektorientierung wie sie C++, Java, C# und auch VB vorgeben, ist einfach nicht für Flexibilität und Evolvierbarkeit gedacht gewesen. Und da Sprache unser Denken bestimmt, kommen bei der Benutzung objektorientierter Sprachen eben nicht einfach flexible und evolvierbare Programme heraus.

Objektorientierung ist sozusagen die Brille der Programmier-Behavioristen. Wer durch sie blickt, vereinfacht die Welt extrem – manchmal bis zur Unkenntlichkeit. Wo die Welt der Behavioristen durch Reiz-Reaktion determiniert ist, da ist sie für “die Objektorientierten” durch Zustand und Synchronizität geprägt.

image Und so wie Behaviorismus bei der Dressur eines Tanzbären funktionieren mag, so funktioniert Objektorientierung auch bei einem simplen Malprogramm. Ok, sie “funktioniert” auch bei viel größeren Anwendungen – doch ich meine mit “funktionieren” nicht, dass objektorientierter Code am Ende läuft, sondern ob mit den Mitteln der Objektorientierung etwas heraus kommt, was sich auch weiterentwickeln lässt. Denn das ist ein wesentliches und trotzdem oft unterschätztes Qualitätsmerkmal von Software. Beim Tanzbären ist das hingegen nicht wichtig. Wenn der keine neuen Tricks mehr lernt, nimmt man einen neuen.

Wer hingegen mehr als einen Tanzbären dirigieren will, z.B. ein ganzes Unternehmen, der ist mit Behaviorismus schlecht bedient. Unternehmensführung funktioniert schlecht mit Zuckerbrot und Peitsche als Mittel, um Reize auszulösen und Reaktionen zu provozieren, die eine vielköpfige Belegschaft verlässlich zum Erfolg führen.

Dito die Objektorientierung in nicht mehr einfach überschaubaren Szenarien. Direkte, streng typisierte und synchrone Kopplung ist das falsche Denkmuster für evolvierbare Software. Solche Kopplung ist aber der Default der Objektorientierung. Nur mit Mühe und Disziplin kann man sich von ihm lösen. Das mag dann zwar das Expertentum von Softwareentwicklern ausmachen – doch wie in Panik die Moral schnell verdampft, so verflüchtigen sich unter Projektdruck auch solche programmierzivilisatorischen Errungenschaften. Was dann nicht durch die Sprache quasi erzwungen wird, das findet nicht statt.

Deshalb bin ich für möglichst hart verdrahtete neue Defaults – wenn schon nicht neue Sprachen, die die Evolvierbarkeit begünstigen. Ein solcher Default ist die echte Komponentenorientierung, bei der Komponentenimplementationen physisch alleingestellt in Komponentenwerkbänken stattfinden und Komponenten einander nicht mehr direkt referenzieren. Deshalb bin ich für Flows als ersten groben Implementationsansatz für jede Realisierung eines Features; denn in Flows gibt es keine Kopplung mehr zwischen den Schritten, so dass die Evolvierbarkeit sehr hoch ist. Deshalb bin ich für die Nutzung eines in-proc Bussystems, d.h. selbst wenn die Kommunikation noch lokal und synchron ist. Ein solcher Bus entkoppelt noch mehr als ein DI Container.

Komponentenorientierung begrenzt die OO-Defaults lokal auf die Implementation einer überschaubaren Komponente. Und DI Container, Flows sowie Busse sind neue Defaults für die Kopplung von Strukturen, wenn es unüberschaubar wird – was immer schneller passiert, als wir uns vorstellen.

Wie wäre es, damit einmal im neuen Jahr zu experimentieren? Der überkommene Programmierbehaviorismus würde einem differenzierteren Verständnis von Software weichen.

Samstag, 7. November 2009

Zustand als Abhängigkeit – IoC konsequent gedacht [OOP 2010] [endlich-clean.net]

Design for Testability ist auch Design for Flexibility. Deshalb liebe ich TDD. Allerdings bin ich dabei immer wieder über einen Problemfall gestolpert: die Prüfung des Zustands eines system under test (SUT). Als Beispiel hier ein typischer (nicht unbedingt optimaler) Ansatz bei der bekannten und beliebten KataPotter.

Viele denken bei der Implementierung der Preisberechnung für die KataPotter an einen Warenkorb, den es zu füllen gilt, um dann den Preis unter Anwendung der Rabatte zu berechnen. Sie definieren eine solche Klasse dann z.B. so:

class KataPotterShoppingBasket

{

    private Dictionary<int, int> books = new Dictionary<int, int>();

 

    public void AddBook(int bookId)

    {

        //...

    }

 

    public decimal Total

    {

        get

        {

            //...

        }

    }

}

Ein  zugehörige Test scheint zunächst einfach:

[TestFixture]

public class testKataPotterShoppingBasket

{

    [Test]

    public void Enthält_ein_Buch()

    {

        var sut = new KataPotterShoppingBasket();

 

        sut.AddBook(1);

 

        //?

    }

}

Aber, ach, ohje… Wie prüft man denn nun, ob das hinzugefügte Buch auch im Warenkorb angekommen ist? AddBook() ist ein Kommando und verändert den Zustand des Warenkorbs. Wie kann man also ganz allgemein diesen Zustand in Tests prüfen?

Zugriffsroutinen für Zustand?

Naheliegend ist die Prüfung von Zustand durch direkten Zugriff auf den Zustand. Dafür muss das SUT nur einen solchen Zugriff anbieten. Für einen üblichen Warenkorb mag das der Fall sein. Da gibt es nicht nur ein AddBook(), sondern sicher auch noch ein GetBook() oder eine ganze Collection von Books, über die man prüfen kann, ob eine Zustandsveränderung wirksam war.

Der KataPotter-Warenkorb braucht solche Zugriffsroutinen aber nicht. Warum sollten sie also nur für den Test eingeführt werden? Ich halte nichts davon, weil sie das Interface des system under test aufblähen. Sie haben keinen Wert für die Domäne, sind in der Intellisense-Liste der Eigenschaften und Methoden aber immer sichtbar. Da hilft auch die Deklaration als intern nicht viel. Das wäre falsch verstandenes Design for Testability.

Wenn Sie die KataPotter so wie oben gezeigt angehen sollten, widerstehen Sie der Versuchung, dem Warenkorb z.B. eine Count-Eigenschaft zu geben, um den Test zum Laufen zu bekommen.

Solch ein Design wäre dann zwar irgendwie testbarer, aber die Flexibilität würde sich nicht erhöhen. Sie hätten nicht mehr das minimal Notwendige getan. Außerdem würde der Test dann zweierlei testen. Das ist zu vermeiden! Er würde AddBook() und Count prüfen. In beiden könnten ja Fehler stecken.

AddBook() und Count wären sogar gegenseitig abhängig in Bezug auf Tests. Sie könnten auch Count nicht zuerst testen, denn dazu müssten Sie Zustand aufbauen. Wäre das nicht der Fall, ist die Nutzung von mehreren SUT-Funktionalitäten in einem Test nicht so schlimm. Dann können Sie die in separaten Tests vorweg prüfen.

Ergebnis: Vermeiden Sie, spezielle Zugriffsmethoden für Zustand nur zum Zweck des Testens einzurichten. Allemal, wenn sie sie nicht wirklich isoliert testen können.

Zustandstest durch Ableitung?

Auf Zustand können Sie aber nicht nur von außen zugreifen, einfacher ist es von innen. Eine Alternative zu Zugriffsmethoden wäre daher die Ableitung einer Klasse vom SUT. Deren Zustand wäre ja der des Warenkorbs. Dazu müssten Sie nur die Sichtbarkeit des Zustands ändern:

class KataPotterShoppingBasket

{

    protected Dictionary<int, int> books = new Dictionary<int, int>();

    ...

}

Eine Ableitung kann dann “gefahrlos” eine Zugriffsmethode anbieten. Die “verunreinigt” die Domänenklasse nicht:

class TestableShoppingBasket : KataPotterShoppingBasket

{

    public int Count { get { return base.books.Count; } }

}

Wenn Sie im Test die abgeleitete Klasse als SUT instanzieren, haben Sie also Testbarkeit “ohne Reue”:

[Test]

public void Enthält_ein_Buch()

{

    var sut = new TestableShoppingBasket();

 

    sut.AddBook(1);

 

    Assert.That(sut.Count, Is.EqualTo(1));

}

Mit einer Ableitung müssen Sie nicht einmal Design for Testability betreiben. Und die Flexibilität wird auch nicht eingeschränkt. Eine scheinbar ideale Lösung, oder? Der Eingriff, private Variablen auf protected statt private zu setzen, ist minimal. Und dass dieser Ansatz an abgeschlossenen (sealed) Klassen scheitert, ist auch unbedeutend.

Dennoch will mir eine Ableitung vom eigentlichen SUT nicht recht schmecken. Nein, das liegt nicht daran, dass ich Ableitungen für generell überbewertet halte. Hier wäre ja sogar das Liskov Substitution Principle eingehalten. Den Aufwand für die Ableitungsklasse finde ich auch nicht zu hoch.

Nein, es etwas anderes, das mich stört…

Zustand als Abhängigkeit

Ich glaube, mich stört, dass Zustand hier eine Extrawurst gebraten bekommt. Zustand wird nicht seiner Natur nach behandelt. Denn Zustand ist eine Abhängigkeit!

Üblicherweise denken wir bei Abhängigkeiten an Funktionalität. Der Warenkorb könnte z.B. von einer Preisberechnungsfunktionalität abhängen:

image

Dann fänden wir es ganz natürlich, diese Abhängigkeit in den Warenkorb zu injizieren:

class KataPotterShoppingBasket

{

    private IPriceCalculator calc;

    private Dictionary<int, int> books = new Dictionary<int, int>();

 

    public KataPotterShoppingBasket(IPriceCalculator calc)

    {

        this.calc = calc;

    }

    ...

Aber was ist jetzt das private Feld calc? Es gehört genauso zum Zustand des Warenkorbs wie books. Der einzige Unterschied  besteht darin, dass calc von einem selbstdefinierten Typ ist und book einen .NET Fx Typ hat.

Darüber hinaus glauben wir dann aber irgendwie noch eine größere “Intimität” zwischen book und z.B. AddBook() zu spüren. Die Inhalte von book werden mittels der Warenkorbmethoden verändert, während calc konstant bleibt. book ist eben “wahrer” Zustand und deshalb anders zu behandeln als calc.

Doch warum ist die Abhängigkeit von einem Preisberechner denn offengelegt? Warum wird der injiziert? Weil das den Warenkorb isoliert testbar macht. Sie können ihn unter Injektion einer Attrappe auf die Probe stellen. Dazu kommt, dass damit die Preisberechnung auch noch flexibler austauschbar wird; ein schöner Nebeneffekt.

Wenn nun jedoch bessere Testbarkeit zur IoC für funktionale Abhängigkeiten geführt hat, warum sollte sie dann nicht auch bei Abhängigkeiten von Zustand zum Einsatz kommen? Ich sehe da kein gewichtiges Gegenargument. Das widerspräche auch nicht der Objektorientierung. Nach Injektion ist Zustand ja weiterhin in einem Objekt entsprechend dessen Zugriffsmethoden verborgen.

Wenn wir von Abhängigkeiten sprechen, sollten wir also konsequent sein. Zustand (state) wie Funktionalität (behavior) sind zwei Seiten derselben Medaille Abhängigkeit:

image

Aus meiner Sicht sieht die Lösung des Ausgangsproblems deshalb so aus:

class KataPotterShoppingBasket

{

    private readonly PriceCalculator calc;

    private readonly Dictionary<int, int> books;

 

    public KataPotterShoppingBasket(Dictionary<int, int> books,

                                    PriceCalculator calc)

    {

        this.calc = calc;

        this.books = books;

    }

    ...

Dem erweiterten Warenkorb injizieren Sie nicht nur die Abhängigkeit von einer Preisberechnungsfunktionalität, sondern auch einen initialen Zustand:

[Test]

public void Enthält_ein_Buch()

{

    var books = new Dictionary<int, int>();

    var sut = new KataPotterShoppingBasket(

                    books,

                    null);

 

    sut.AddBook(1);

 

    Assert.That(books.Count, Is.EqualTo(1));

}

Das halte ich für genauso konsequent wie einfach zu verstehen. Etwas systematisiert lautet dann das Muster:

  1. Abhängigkeiten einer Klasse zusammenfassen in einer Abhängigkeitsklasse. Das gilt für Zustände wie Funktionalität, von denen eine Klasse abhängt.
  2. Während Tests die dafür minimal nötigen Abhängigkeiten mit einer Instanz der Abhängigkeitsklasse injizieren.
  3. Erwartungen an Zustandsänderungen nach Ausführung von Kommandos auf dem SUT prüfen anhand der injizierten Abhängigkeitklasseninstanz.

Am Beispiel des Warenkorbs sähe das so aus:

class KataPotterShoppingBasket

{

    internal class Dependencies

    {

        public readonly IPriceCalculator calc;

        public readonly Dictionary<int, int> books =

    }

 

 

    private readonly Dependencies dependencies;

 

    public KataPotterShoppingBasket(Dependencies dependencies)

    {

        this.dependencies = dependencies;

    }

    ...

Der Test wäre sogar noch ein wenig klarer, finde ich:

[Test]

public void Enthält_ein_Buch()

{

    var dependencies = new KataPotterShoppingBasket.Dependencies();

    var sut = new KataPotterShoppingBasket(dependencies);

 

    sut.AddBook(1);

 

    Assert.That(dependencies.books.Count, Is.EqualTo(1));

}

Diesem Verfahren steht auch die “normale” Dependency Injection nicht im Wege. Sie würde es sogar verbergen. Dazu bräuchte die Abhängigkeitsklasse nur einen ausgewiesenen “Kanal” für die Injektion der Funktionalität, von der der Warenkorb abhängig ist, z.B. so für Unity als DI Container:

public class Dependencies

{

    [Dependency]

    public IPriceCalculator calc {get; set;}

    public readonly Dictionary<int, int> books = …

}

Zusätzlich müsste im Mapping auch die Abhängigkeitsklasse aufgeführt werden. Aber das ist kein sonderlicher Mehraufwand. Das ließe sich womöglich sogar automatisieren.

Fazit

Im Sinne eines systematischen Vorgehens beim Test und beim Design finde ich es sehr überlegenswert, Abhängigkeiten breiter als bisher zu fassen. Funktionseinheiten sind nicht nur von anderen Funktionseinheiten abhängig, sondern auch von Zustand.

Wenn dann Abhängigkeiten injiziert werden sollen, um bessere Testbarkeit zu erreichen, dann gilt das nicht nur für Funktionseinheiten, sondern auch für Zustand.

Gerade bei TDD sinkt dadurch – so mein Empfinden – der gedankliche Aufwand. Bei jedem Kommando und jeder Abfrage, die getestet werden, müssen Sie sich dann einfach nur die Frage stellen, welcher Zustand dabei eine Rolle spielt. Dann bauen Sie vor dem Test Ausgangszustand speziell für den Testfall zusammen (arrange) und injizieren ihn, rufen das SUT (act) auf und prüfen anschließend (bei Kommandos) den neuen Zustand (assert). Fertig.

Ich werde in der nächsten Zeit mal probeweise konsequent so vorgehen. Mal schauen, wie sich das anfühlt. Ich bin optimistisch, dass es ein gutes Gefühl sein wird. Weniger Zweifel, weniger Nachdenken, also höhere Produktivität.

Samstag, 31. Oktober 2009

TDD wofür? [OOP 2010] [endlich-clean.net]

Anlässlich einiger Code Katas, die ich in letzter Zeit gemacht habe, hier ein paar Gedanken zu Nutzen und Grenzen von TDD (Test-Driven Development oder Test-Driven Design):

Was leistet ein Test? Er prüft die Korrektheit der Funktionsweise einer Codeeinheit. Klingt selbstverständlich. Die Erkenntnis durch TDD liegt für mich aber in der Umkehrung: Nur, was als eigene Codeeinheit vorliegt, kann auch direkt auf Korrektheit geprüft werden.

Codeeinheiten sind mehr oder weniger kompliziert. Eine ganz einfache könnte so aussehen:

public int Add(int a, int b)
{
    return a-b;
}

Ist die korrekt? Das stellt ein genauso einfacher Test leicht fest:

[Test]
public void Adding_two_numbers()
{
    var sut = new MyMath();
    Assert.AreEqual(5, sut.Add(2, 3));
}

Wenn der Testbalken jetz rot ist, dann lokalisiert eine Codeinspektion schnell den Übeltäter.

Wie steht es aber mit diesem Code zur FizzBuzz Kata:

IEnumerable<string> FizzBuzz()
{
    for(int i=1; i<=100; i++)
    {
        if (i%3 == 0)
           yield return “Fizz”;
       else if (i%5 == 0)
           yield return “Buzz”;
       else if (i%3 == 0 && i%5 == 0)
           yield return “FizzBuzz”;
       else
          yield return i.ToString();
    }
}

Ist der korrekt? Wer kann das auf einen Blick sehen? Vor allem aber, wer kann ihn so einfach runterschreiben? Die Entwicklung geht ja eher schrittweise vor. Genügt da ein Test wie:

[Test]
public void Test_FizzBuzzer()
{
    var sut = new FizzBuzzer();
    string expectedValues = new[] string {“1”, “2”, “Fizz”, “4”, “Buzz”, …};
    int i;
    foreach(string v in sut.FizzBuzz())
        Assert.AreEqual(expectedValues[i++], v);
}

Mit dem kann man natürlich von Anfang an “draufhauen” auf das system under test (SUT). Aber wenn irgendwas nicht klappt, weiß man nicht so recht, wo es hapert. Die SUT-Codeeinheit ist unübersichtlich und damit der Test sehr pauschal.

Durch TDD entsteht ein Gefühl dafür, wenn es unübersichtlich wird. Dann testen die Tests nämlich nicht mehr nur eine Funktionalität, sondern mehrere. Im obigen Fall ist der Test sozusagen ein Integrationstest, der sowohl die Generierung von Zahlen wie auch die Transformation einer Zahl prüft. Ob es ein Problem mit den Zahlen gibt oder eines bei der Transformation lässt sich im Zweifelsfall nur schwer sehen. Für mehr Klarheit bietet sich dann der Debugger an.

Besser ist es jedoch, den Debugger in der Schublade zu lassen. Stattdessen lieber den Code in Teilfunktionen zerlegen. Denn: Nur, was als eigene Codeeinheit vorliegt, kann auch direkt auf Korrektheit geprüft werden.

Die Teilfunktionen hier im Beispiel: Zahlengenerierung und Transformation. Die könnten mit ihrer Signatur so aussehen:

IEnumerable<int> ErzeugeZahlenreihe() { … }

string TransformiereZahl(int n) { … }

Daraus folgen dann insgesamt drei Tests:

  1. Prüfe die Zahlengenerierung
  2. Prüfe die Transformation
  3. Prüfe die Integration von Zahlengenerierung und Transformation

Jeder Test ist nun sehr spezifisch und vergleichsweise einfach. Dass das zu etwas mehr Code führt, ist nicht so schlimm, wie sich gleich zeigen wird. Geht etwas schief, ist der Scope jedenfalls klein. Ein Debugger muss nur selten angeworfen werden, um den Fehler darin zu finden. Das erhöht die Entwicklungsgeschwindigkeit wieder.

Wieviel Rätselraten Sie betreiben wollen, wenn in Ihrem Code etwas schief geht, bestimmen also sie. Der Rätselspaß (oder –ärger?) ist proportional zur Größe Ihrer SUTs.

Zudem gilt: Die Integration einzeln getesteter und für korrekt befundener Codeeinheiten zu testen ist weniger aufwändig, als nur die Codeeinheiten verschmolzen zu einem Ganzen zu testen.

Und jetzt zur zweiten TDD-Erkentnis: Code in einer eigenen Einheit ist prinzipiell austauschbar. Nicht nur lässt er sich gezielt testen und damit kontrolliert verändert werden. Er lässt sich auch gezielt komplett ersetzen.

In Max Frischs “Andorra” steht – wenn ich mich recht erinnere: “Nur was verzapft ist, geht nicht aus dem Leim.” Ein Satz, der mir irgendwie aus dem Deutschunterricht im Gedächtnis geblieben ist, auch wenn die Lektüre ansonsten keinen bleibenden Eindruck bei mir hinterlassen hat.

Angelehnt an diesen Satz, möchte ich vor dem TDD-Hintergrund formulieren und hoffen, dass es sich bei Ihnen ebenso einprägt: “Nur was isoliert testbar ist, kann ersetzt werden.” Das ist der Grund, warum aus Test-Driven Development über die Zeit Test-Driven Design geworden ist. Denn TDD macht es nicht nur leichter, die Korrektheit von Code zu prüfen, weil es testbare Codeeinheiten “austreibt”. Indem es diese Codeeinheiten motiviert, erzeugt TDD Flexibilität.

Am Beispiel: Gibt es erstmal eine eigene Codeeinheit für die Zahlengenerierung und die Transformation, dann kann es dafür alternative Implementationen geben. Die Zahlengenerierung könnte heute die natürlichen Zahlen erzeugen und morgen nur Primzahlen oder nur Fibonacci-Zahlen. Die Transformation könnte heute Vielfache von 3 und 5 erkennen und morgen gerade Zahlen oder Primzahlen.

Direkt und isoliert testbare Codeeinheiten haben immer einen irgendwie gearteten Kontrakt. Für die Zahlengenerierung wäre das z.B. Func<IEnumerable<int>>. Solange eine Integration nur auf die Kontrakte der sie konstituierenden Codeeinheiten abhebt, ist sie also frei, jede Implementation, die ihn erfüllt, zu nutzen.

Ergo: TDD führt nicht nur zu einfacherer Testbarkeit aufgrund kleinerer Codeeinheiten, sondern auch zu größerer Flexibilität.

Doch damit nicht genug! Ebenfalls segensreich empfinde ich, dass TDD quasi sofort Ergebnisse liefert. Wer erst lange an einer Implementation rumschraubt und am Ende testen will, der verschiebt die Befriedigung eines grünen Testbalkens auf unbestimmte Zeit. Implementationsperioden ohne Testläufe sind Durststrecken. TDD ist also eine ganz zeitgemäße “Genuss sofort”-Technik. Warum sich die Befriedigung eines grünen Testbalkens versagen? Aber vor allem: Warum länger als unbedingt nötig im Nebel tappen? Denn je länger Sie ohne Testlauf implementieren, desto tiefer wandern Sie in einen Nebel der Ungewissheit hinein. Ist das, was Sie an Code schreiben, tatsächlich zielführend? Löst Ihr Code wirklich das Problem? Oder schreiben Sie überhaupt den Code, den Sie schreiben wollen? Implementieren Sie tatsächlich Ihr Konzept?

TDD führt also auch zu häufigem Realitätscheck. Das ist umso wichtiger, je unbekannter die Problemdomäne. Wie sich in CodingDojos auf dem .NET Open Space und der prio-Konferenz herausgestellt hat und auch Übungen während Clean Code Developer Trainings zeigen, lauert die Unbekanntheit überall. Kaum ein Problem ist so einfach, dass sich TDD erübrigen würde. Nehmen Sie dafür aber nicht mein Wort, sondern probieren Sie es aus. Die KataPotter ist dafür ein Beispiel, das schon so manchem einiges Stirnrunzeln gemacht hat. Oder versuchen Sie sich am Minesweeper. Auch schön ist der Umgang mit römischen Zahlen.

Apropos römische Zahlen: Hier sehe ich allerdings durchaus eine Grenze von TDD. TDD hilft, separate Funktionseinheiten in einer größeren zu erkennen. Wird die größere schwer zu testen, dann liegt eine Zerlegung nahe.

TDD hilft jedoch nicht weiter, wenn eine Codeeinheit quasi unteilbar ist. Das ist oft nur scheinbar der Fall – am Ende ist jedes Lösung ja aber ein endlicher Baum, dessen Blätter eben die “atomaren” Codeeinheiten sind. Wie solche “atomaren” Codeeinheiten intern aussehen, beantwortet TDD nicht. TDD ist ein Hilfsmittel zur Strukturierung, nicht für den Algorithmenentwurf. Ein Bubblesort-Algorithmus lässt sich mittels TDD vielleicht noch entwickeln, weil er sich in naheliegende Codeeinheiten zerlegen lässt; ein Quicksort-Algorithmus jedoch ist jenseits von TDD.

Die Kata zur Umwandlung von römischen Zahlen profitiert mithin nicht so stark von TDD wie z.B. FizzBuzz oder KataPotter. Sie ist eher algorithmischer, denn struktureller Natur.

Und so bin ich bei meiner abschließenden Erkenntnis zu TDD: TDD ist kein Ersatz für Kreativität, Nachdenken und Erfahrung. Auch mit TDD als Werkzeug im Programmierkoffer lohnt der Einsatz des Gehirns vor dem Programmieren. Nachdenken hilft und verkürzt die Lösungszeit. Angesichts von z.B. der Minesweeper-Kata müssen wir uns nicht dümmer stellen als wir sind. Wir müssen nicht reflexartig in die Tasten hauen und sofort einen Test schreiben. Es lohnt sich, einen Moment über das Problem nachzudenken. Welche Datenstrukturen sind im Spiel? Welche Algorithmen? Wie könnte eine Grobstruktur der Lösung aussehen, welche Codeeinheiten/Verantwortlichkeiten könnten wie zusammenspielen?

Wenn Sie dann einen gewissen Plan haben, dann erst legen Sie mit TDD los. Und zwar gehen Sie dann gezielt mit TDD auf die Grobstrukturen los, die Sie durch Nachdenken ermittelt haben. Das erspart unnötigen Refaktorisierungsfrust.

Zum Schluss eine offene Frage: Erzeugt TDD nicht nur testbare und flexible Strukturen, sondern auch evolvierbaren Code? Ist Testbarkeit bzw. Flexibilität mit Evolvierbarkeit gleichzusetzen? Oder könnte eine Zerlegung, die nicht durch TDD motiviert ist, noch evolvierbarer sein? Hm…

Einstweilen entscheide ich mich allerdings im Zweifel für den Glauben an TDD gepaart mit Nachdenken. Für die Strukturierung von Komponenten ist das ein sehr hilfreiches Vorgehen.

Sonntag, 25. Oktober 2009

Le manifeste du jour – Was will uns die SOA-Welt sagen? [OOP 2010]

Jetzt ist es wieder passiert: Ein Manifest hat das Licht der Softwarewelt erblickt. Dieses Mal ist es das SOA Manifest. 2001 hatte das Agile Manifest den Anfang gemacht, 2009 kam das Software Craftsmanship Manifest dazu und nun ebenfalls noch in 2009 die SOA-Fraktion. Was ist nur los? Was will uns die SOA-Welt sagen?

Die naheliegende Antwort findet sich natürlich im Manifest selbst. Doch darum geht es mir zunächst noch nicht. Mich interessiert erst einmal, warum es überhaupt ein SOA Manifest gibt.

Also: Wann schreibt “man” ein Manifest? Hier ist “man” eine Gruppe vor allem bestehend aus SOA-Beratern und SOA-Produktherstellern. Wikipedia sagt: “Ein Manifest (lat.: manifestus, ‘handgreiflich gemacht’) ist eine öffentliche Erklärung von Zielen und Absichten, oftmals politischer Natur.” (http://de.wikipedia.org/wiki/Manifest) Das heißt, diese Gruppe möchte sich mit dem Manifest für alle deutlich in ihren Intentionen erklären.

Im Umkehrschluss bedeutet es: Offensichtlich sieht diese Gruppe solch eine Profilierung durch eine manifestierte Erklärung als nötig an. SOA-Berater und SOA-Produkthersteller fühlen sich demnach irgendwie falsch wahrgenommen. Sie greifen zum Mittel des Manifests, um diese Wahrnehmung zu korrigieren. (Dass sie sich nicht wahrgenommen fühlen und mit dem Manifest einen Marketingcoup laden wollen, kann ich nicht glauben. Dafür ist der Kreis der ursprünglichen Unterzeichner zu illuster. Man kennt sie schon.)

Das finde ich nun spannend: Die SOA-Proponenten fühlen sich falsch wahrgenommen. Hm… sehr interessant. Wie kann das denn sein? Wer hat denn die Wahrnehmung beeinflusst? Woher kommt die Wahrnehmung von SOA in den Unternehmen (oder auch der Entwicklergemeinde allgemein)? Haben die Gruppenmitglieder sie nicht entscheidend mitgeprägt? Und jetzt brauchen sie ein Manifest, um diese mitgestaltete Wahrnehmung zu korrigieren?

Oder ist ihnen die Gestaltung der Wahrnehmung aus der Kontrolle geraten? Hat sie sich verselbstständigt? Sind die SOA-Proponenten selbst Opfer eines Hypes geworden? Zauberlehrlinge, denen die SOA-Geister, die sie riefen, nicht mehr gehorchen wollten? Und jetzt soll das Manifest die Geister bannen?

Aber mal den Spott beiseite: Es könnte natürlich auch sein, dass das Manifest sozusagen ein “refactoring to deeper insight” ist. Nach Jahren der SOA-Geschäftigkeit hat die SOA-Gemeinschaft nun herausgefunden, worum es wirklich, wirklich geht bei SOA. Das wollen sie nun mit einem Manifest kurz, knapp, präzise ausdrücken. Endlich.

Standen am Anfang technische Gesichtspunkte wie die “Four Tenets of SOA”, geht es jetzt um mehr: es geht ums Business, es geht um Strategie. Ach! Wer hätte das gedacht. (Sowas, jetzt werde ich wieder ein wenig spöttisch. Das wollte ich doch gar nicht.)

Nun bin ich nach der Frage der Motivation doch bei den Inhalten angelangt. Die sind:

  • Business value over technical strategy
  • Strategic goals over project-specific benefits
  • Intrinsic interoperability over custom integration
  • Shared services over specific-purpose implementations
  • Flexibility over optimization
  • Evolutionary refinement over pursuit of initial perfection

Im Stil des agilen Manifests gibt es wieder eine linke und eine rechte Seite. Rechts steht das, was bisher wichtig erschien, links das, was jetzt noch wichtiger erscheint. Und was bedeutet das, wenn links jetzt plötzlich wichtiger als rechts ist? Meine Interpretation:

  • Die SOA-Proponenten leiden darunter, dass sie in eine Technologieecke gestellt werden. Aus der wollen sie raus. Sie betonen also, dass es ihnen um ein besseres Geschäft des Kunden geht, wenn SOA eingeführt wird.
    Wenn es Beratern und Technologieherstellern um ein besseres Geschäft ihrer Kunden geht, ist das natürlich sehr löblich. Bedenklich allerdings finde ich, dass das herauszustellen ist. War es bisher anders? Sind die SOA-Proponenten nun endlich ethisch erwacht? Von den Beratern hätte ich das allemal immer schon gedacht (oder zumindest erhofft), von den Technologieherstellern kann ich es so ganz jedoch nicht wirklich glauben. Wenn IBM, Microsoft und Oracle sagen, es ginge ihnen bei SOA um “Business value” und nicht so sehr um die Technik, dann müssten sie sich schon sehr zusammenreißen in SOA-Beratungsgesprächen. Womöglich müssten sie sagen: “Ja, verzichtet mal auf unser Produkt, denn ‘Business value’ könnt ihr auch auf andere Weise herstellen.” Hm…
    Oder steht der “Business value” einfach nur an erster Stelle im Manifest, weil man wieder – wie schon so oft – versucht, den geplagten IT-Entscheidern klar zu machen, dass es keine technische Silberkugel gibt? Wäre möglich – fände ich nun aber gar nicht neu.
  • Die SOA-Proponenten scheinen auch darunter zu leiden, nicht von “ganz oben” in den Unternehmen eingekauft zu werden. Oder sie werden aus ihrer Sicht nicht breit genug eingesetzt. Sie fühlen ihre Bemühungen zu sehr auf einzelne Projekte konzentriert, statt dass man ihnen das große Ganze anvertraut.
    Das wundert mich, denn SOA hat doch quasi vom ersten Tag an immer klar gemacht, dass es ums Übergreifende geht, um die Strategie. Das soll missverstanden worden sein? Oder sehen sich Unternehmen eben doch nicht in der Lage, bei großen IT-Landschaften mit einer umfassenden Strategie alles auf SOA umzukrempeln? Gehen sie, wenn sie eine Projektbrille aufsetzen, eher einen pragmatischen Weg? “Towards SOA one project at a time” hört sich für mich zumindest nicht so schlecht an. Von großen Visionen, die dann scheitern, hatten wir schon genug.
    Allemal den Technologieherstellern unter dem Manifest kann es ja nur recht sein, wenn man sie (endlich) auf strategischer Ebene als Gesprächspartner sieht. Da lässt sich gleich viel besser ein Servicebus oder eine Datenbank durchs ganze Unternehmen treiben.
  • Interoperabilität von vornherein, Interop eingebaut: Das wünschen sich die SOA-Manifestler. Statt also auf einer “as needed” Basis zu integrieren, wollen sie vorausdenken. Das passt natürlich zur strategischen Bedeutung, die SOA haben soll.
    Ganz grundsätzlich ist gegen Offenheit oder Standards natürlich nichts zu sagen. Interoperable by design ist irgendwie ne gute Sache. Mich wundert nur wieder, dass das erstens überhaupt erwähnt werden muss. Stand Interoperabilität nicht vom ersten Tag im Kern von SOA? Warum muss man jetzt von einer Erkenntnis sprechen, dass Interop von vornherein mitgedacht werden soll (“Through our work we have come to prioritize…” [meine Hervorhebung])?
  • Was ist der Zweck eines SOA-Service? Dass er von möglichst vielen genutzt wird. So stand es am Anfang von SOA. So heißt es nun immer noch. Nichts Neues also in der SOA-Welt. Wie kann das ein Erkenntnisgewinn hingestellt werden?
    Doppelt berechtigt ist diese Frage, da einer der Unterzeichner – Nicolai Josuttis – schon deutlich erklärt hat, dass Wiederverwendbarkeit eben nicht der “Business case” von SOA sein kann, weil die Wiederwendungszahlen für SOA-Services so gering sind. Also was nun? “Shared services”, hohe Wiederverwendbarkeit, oder doch nicht?
  • Flexibilität – so die Manifesterkenntnis – ist wichtig bei SOA. Wer hätte das gedacht? Stand auch das nicht schon am Anfang allen Servicedenkens? Mir kommt es so vor, dass auch hier ein Lernprozess überflüssig war. Für mich gehört Flexibilität zum Kern von SOA.
    Vor allem ist merkwürdig, dass Flexibilität mit Optimierung verglichen wird. Das ist für mich ein Kategorienfehler. Denn Flexibilität ist nur eine Qualität neben Performance, Skalierbarkeit, Sicherheit, die ebenfalls optimiert werden kann. Wer “Flexibility over optimization” ausruft, der läuft für mich Gefahr, die Flexibilität zu überoptimieren. Sie ist jedoch genausowenig Selbstzweck wie Performance oder Robustheit – auch wenn Flexibilität zum Zweck von SOA gehört.
  • Der Evolvierbarkeit eine Stimme zu verleihen, finde ich natürlich wunderbar. Dem letzten Punkt des Manifests stimme ich voll zu. Nur finde ich diese Forderung weder neu – erinnert mich irgendwie auch an “Responding to change over following a plan” im agilen Manifest –, noch finde ich sie in einem SOA-Manifest besonders passend. Sie ist schlicht zu allgemein, zu unspezifisch. Sie hat nichts mit SOA im Speziellen zu tun. Warum sie also hier als etwas Hervorhebenswertes aufnehmen? Und wieder: Wie kann das eine neue Erkenntnis sein, die die SOA-Proponenten erst durch ihre spezielle SOA-Praxis gewonnen haben (“…we have come to prioritize…”)?

Mein Fazit: Das SOA-Manifest kommt nicht wirklich los von dem, was es wohl loswerden will: vom Hype. Wenn ich mal den günstigsten Fall annehme, dass das SOA-Manifest ein Versuch ist, durch den Hype-Nebel der letzten SOA-Jahre zu sehen und den Kern von SOA nun endlich herauszuschälen, dann geschieht das allerdings letztlich auf gleiche Weise, durch Hype. Vielleicht liegt das an Conway´s Law? Danach könnte eine essenziell auf Hype aufgebaute “Organisation” (wie die der SOA-Proponenten, die ja bei großen Unternehmen für den großen “Business value” die große Strategie mitbestimmen und deshalb große Aufmerksamkeit bei den CTOs und CEOs braucht) eben auch bei allem guten Willen nur wieder Hype-Produkte herstellen. Sie kann nicht anders. Wie tragisch.

Ganz unabhängig von jeder Intention finde ich die Inhalte des Manifests aber auch nicht spannend. Mir fehlt es an Überraschungswert. Wo ist der echte Erkenntnisgewinn? Ich fühle mich nicht zu Ahs und Ohs hingerissen. Entweder geht es bei den Statements um das, was von der ersten Minuten SOA ausgemacht hat. Oder es geht um Selbstverständliches, von dem verwunderlich ist, dass ein Erkenntnisprozess von mehreren Jahren dazu nötig war. Schade.

Vielleicht bin ich aber auch nur grummelig, weil ich selbst noch kein Manifest unterzeichnet oder gar erarbeitet habe? Hm… Ich muss mal die Clean Code Developer (CCD) Community befragen. In CCD schlummert bestimmt Potenzial für mindestens ein Manifest. Das wäre doch gelacht. Was die Software Craftsmen und die SOA-Welt können, das können wir auch. Noch in 2009 oder lieber warten auf 2010? Wäre doch gelacht… ;-)

Mittwoch, 14. Oktober 2009

Stirnrunzeln über das Neueste von Microsoft seit geschnitten Brot [OOP 2010]

Grad scheint Microsoft wieder mal “the best thing since sliced bread” erfunden, wie es scheint. Der Reactive Framework (Rx) zaubert jedenfalls einiges breites Grinsen auf Gesichter bei Microsoft, hier der Erfinder des Ganzen, Erik Meijer, in einem Interview über Rx.

image
(Achtung: Dieses Interview ist auf sehr hohem Abstraktionsniveau! Eine Beschreibung von Rx für Praktiker sucht man hier vergebens.)

Den schätze ich eigentlich sehr, nicht nur für sein früheres “brainchild” Linq. In der letzten Zeit zaubert Erik Meijer aber zumindest mir kein beseeltes Grinsen mehr auf Gesicht, sondern Stirnrunzeln. Da war zuerst das ganz unglückliche Project Volta, welches fundamentalen Erkenntnissen zu verteilten Anwendungen zuwiderlief und jetzt zurecht offline ist. Und jetzt kommt der Reactive Framework.

Was ist der Rx? Das kann man hier und hier recht gut lesen. Ich möchte das jedoch lieber nicht selbst wiedergeben. Denn für mich besteht das Problem darin, dass ich neues Vokabular für etwas Altes benutzen müsste. Meine Kritik am Rx ist: es ist eben keine tolle neue Erfindung seit geschnitten Brot, sondern etwas lange Bekanntes in aufgehübschtes Gewand kleidet.

Mit Rx ist Microsoft aus meiner Sicht nur über Complex Event Processing (CEP) gestolpert. Wenn Erik Meijer die Rx-Interfaces als komplementär zu IEnumerable/IEnumerator ansieht und damit Linq-Queries auf Strömen von Events visioniert, dann ist das nichts anderes als eben CEP: statt auf fixen Daten Queries zu fahren, liest man Ströme von Daten ein und verarbeitet sie mit Queries. Das ist nicht neu, das ist kein Hexenwerk.

Wer wirklich einmal ausprobieren will, was man mit einer Abfragesprache auf Event-Strömen tun kann, der kann mit NEsper spielen: http://esper.codehaus.org/. Das ist (zusammen mit seinem Java-Bruder Esper) ein enterprise ready Open Source CEP Framework. Hier ein kurzes Beispiel für die Ermittlung des Durchschnittspreises “vorbeirauschender” Bestellungen (OrderEvent) innerhalb von jeweils 30-Sekunden-Fenstern.

select avg(price) from org.myapp.event.OrderEvent.win:time(30 sec)

Das ist grundsätzlich nichts anderes tut Rx (s. dazu das Beispiel zur Generierung von Mouse Drag Events hier).

Oder nehmen wir einfach nur Datenflüsse (data flows). Wo ist denn der Unterschied zwischen Rx, das nun wundersamerweise z.B. Mausereignisse als abfragbare Ereignisflüsse sieht? Oder als Flüsse, auf denen man Ereignisbehandlungsroutinen notieren kann. Tut mir leid, da ist nichts neues für mich dabei. Wenn ich im etablierten Paradigma data flow denke, dann ist das etwas uraltes und kann mit heutigen Mitteln bewältigt werden.

Hier ein einfacher Event-Strom mit “Event-Handler”:

new[] {1, 2, 3}.Subscribe(Console.WriteLine);

Der “Event-Handler” braucht nur das altbekannte IEnumerable:

static class EnumExt

{

    public static void Subscribe<T>(this IEnumerable<T> eventStream,
                                    Action
<T> handleEvent)

    {

        foreach (T e in eventStream)

            handleEvent(e);

    }

}

Aber damit nicht genug! Man kann auch IEnumerable-Ströme zusammenstecken zu “Pipes and Filters”. Das ist dann ein Flow. Warum nicht aus einer Zahlenliste die ungeraden herausziehen und negieren?

Zahlen –> UngeradeZahl | ZahlNegieren –> Liste der negierten ungerade Zahlen

Die Formulierung in Code ist denkbar einfach:

new[] {1, 2, 3, 4, 5, 6, 7}
    .Where(n => n%2 != 0)
    .Transform(n => -n)
    .Subscribe(Console.WriteLine);

Und wenn noch ein Schritt dazukommen soll – z.B. die Quadratur der ungeraden Zahlen –, dann wird der einfach eingefügt:

new[] {1, 2, 3, 4, 5, 6, 7}
    .Where(n => n%2 != 0)
    .Transform(n => n*n)
    .Transform(n => -n)
    .Subscribe(Console.WriteLine);

Die Transformationsmethode ist straightforward eine Extension Method wie oben Subscribe():

public static IEnumerable<TOut> Transform<TIn, TOut>(
                 this IEnumerable<TIn> eventStream, 
                 Func<TIn, TOut> processEvent)

{

    foreach (TIn e in eventStream)

        yield return processEvent(e);

}

Wer will, kann sowas auch asynchron machen. Das habe ich schon in früheren Blogposts beschrieben:

http://ralfw.blogspot.com/2009/07/ccr-flows-asynchrone-prozesse-mit-der.html
http://ralfw.blogspot.com/2009/07/verstandnisvorteil-fur-flows.html

Flows habe ich aber natürlich nicht erfunden. Die sind alt. Hier eine Quelle, die das Paradigma schon in den 1970ern propagiert hat: http://jpaulmorrison.com/fbp/. Mainstream ist es aber nicht geworden, wie wir sehen. Schade. Denn in die Schritte in einem Flow steigern die Evolvierbarkeit von Software, finde ich.

Jetzt noch die Umsetzung von Mausbewegungen in einen Strom, den man abfragen kann:

public partial class Form1 : Form

{

    private readonly EventStream mouseMovements = new EventStream();

 

    public Form1()

    {

        InitializeComponent();

 

        this.MouseMove += this.mouseMovements;

    }

    …

Eine Klasse EventStream sorgt dafür, die Mausbewegungen (oder auch andere Events) in ein IEnumerable zu transformieren. Das kann man dann z.B. so durchlaufen:

this.mouseMovements.Events<MouseEventArgs>()
    .Where(args => args.X < 100 && args.Y < 100)
    .Subscribe(args => Console.WriteLine("{0}, {1}", args.X, args.Y));

Wie macht EventStream das? Die Windows Message Loop darf ja nicht aufgehalten werden. Es geht nur asynchron. Also rufe ich die Concurrency Coordination Runtime (CCR) zur Hilfe:

class EventStream

{

    public class Event

    {

        public object Sender;

        public EventArgs Args;

    }

 

    private Port<Event> events = new Port<Event>();

 

 

    private void EventListener(object sender, EventArgs args)

    {

        this.events.Post(new Event{Sender = sender, Args=args});

    }

 

 

    public IEnumerable<TEvent> Events<TEvent>() where TEvent : EventArgs

    {

        Event e;

        while (this.events.Test(out e))

            yield return (TEvent)e.Args;

    }

 

 

    public static implicit operator MouseEventHandler(EventStream stream)

    {

        return stream.EventListener;

    }

    …

}

Mit der impliziten Konvertierung kann ich den EventStream einfach an einen Eventhandler für die Mausbewegungen binden, ohne zu verraten, wer intern die Verarbeitung übernimmt.

Der Eventhandler schiebt dann jeden Event in einen CCR Port. Das ist sogar Thread-sicher. Und wenn ich die aufgelaufenen Events prozessieren will, dann hole ich sie mir über Events() als IEnumerable wieder heraus.

Alternativ könnte ich natürlich auch einen Eventhandler registrieren, der life auf neue Events reagiert und sie z.B. in einen asynchronen Flow einspeist. Dann könnte ich in einer Kette von asynchronen Schritten filtern, transformieren, reagieren. Auch da wären Where(), Transform() und Subscribe() wie oben in den synchronen Beispielen möglich.

Wo also ist das entscheidend neue beim Rx? Beats me. Ich kann nur die grübelnd die Stirn runzeln.

Ok, der Rx mag hier und da etwas einfacher machen, weil er schon Abstraktionen bietet, die ich vielleicht erst bauen muss/müsste. Das wäre ne nette Sache – aber deshalb hat Erik Meijer nun nicht gleich “the best thing since sliced bread” erfunden. Er steht nur in einer langen Tradition. Die zu erwähnen und weniger die Flügel zu schlagen, um nicht zuviel Hype-Staub aufzuwirbeln, das fänd ich angemessen.

PS: Oder übersehe ich hier die eigentliche Erfindung? Geht der brilliante Innovationshub an mir vorbei? Ich bitte um Erhellung.

Sonntag, 11. Oktober 2009

Fokussieren durch bezahlen [OOP 2010]

Wir zahlen zuwenig. Dieser Gedanke ist mir gestern gekommen. Ich hatte mit Google Reader meine RSS-Feeds durchstöbert, dann bei InfoQ geblättert und schließlich eine neue Zeitschrift zur Hand genommen, von der ich ein Probeexemplar bestellt hatte: NOVO argumente. Ganz normal hatte ich also im Überfluss an Lesestoff ein Bad genommen. Ganz normal hatte ich dabei cherry picking betrieben: nur, was mir auf Anhieb interessant erschien, las ich.

Hört sich doch eigentlich auch gut an, oder? Ist es nicht toll, wenn uns soviele Daten “at our fingertips” zur Verfügung stehen? Selbstverständlich. Das möchte ich auch nicht missen. Eine zivilisatorische Errungenschaft ersten Grades.

Und dennoch… Das Bad im Überfluss wollte mich nicht so recht erfrischen. Der Grund: Ich habe mich nicht fokussiert. Ich bin nicht tief gegangen. Ich habe mich nicht verstören lassen.

Meine Tochter hingegen hat nur eine sehr überschaubare Zahl an CDs und Kassetten mit Kindergeschichten wie Bibi & Tina oder Elea Eluander. Die hört sie wieder und wieder. Darin taucht sie tief ein, bis sie jede Einzelheit kennt und mit mir sogar arambolisch spricht. (Arambolisch ist die Sprache einer “Parallelwelt” bei Elea Eluander.)

Der Unterschied zwischen meiner Tochter und mir: Sie lebt im Mangel, ich im Überfluss. Sie taucht tief, ich schwimme an der Oberfläche.

Das ist natürlich eine verkürzte Darstellung. Der überzeichnete Kontrast hilft mir aber, meinen gestrigen Gedanken zu begründen: Ist der Überflüss an Daten vielleicht kontraproduktiv? Haben wir den falschen Wunsch, wenn wir uns noch mehr wünschen? Ist die “Umsonst-Kultur” des Internets nicht nur für Produzenten eine wirtschaftlich schwierige Sache, sondern auch für die Konsumenten?

Mich durchzuckte gestern jedenfalls beim Griff nach der Zeitschrift der Wunsch, es möge mehr kostenpflichtige Angebote geben. Die Zeitschriftenartikel kann ich im Internet kostenlos lesen – wie auch z.B. die der brand eins –, aber ich gebe für sie Geld aus.

Hatte ich zunächst gedacht, das täte ich nur, um in einem Heft bequemer als am Bildschirm lesen können, so sehe ich jetzt einen zweiten positiven Effekt: ich fokussiere mich mehr.

Lesestoff, für den ich bezahle, schenke ich mehr Aufmerksamkeit.

Das ist meine gestrige Erkenntnis. Der Volksmund weiß zwar schon lange, “Was nix kostet, ist auch nix wert”, doch gestern habe ich das echt gespürt, weil ich die Konsequenz gesehen habe. Meine Augen flogen über die kostenlosen Inhalte hinweg. War das Geschriebene zu holprig, bin ich gesprungen oder habe den nächsten RSS-Feedeintrag geöffnet. War ein Video zu langweilig, bin ich gesprungen oder habe das nächste gestartet.

Ohne spürbare Kosten gibt es keinen Anreiz, bei einem Inhalt zu verweilen.

Ist das schlimm? Ich würde sagen, ja, das ist aus mehreren Gründen “schlimm”:

  1. Wir verbrauchen die undiskutierbar endliche Ressource Zeit ineffektiv und ineffizient. Wohlgemerkt beziehe ich mich jetzt auf Zeit, in der wir etwas lernen wollen/sollen. Der Entspannung mag “Zappen” ja (gelegentlich) zuträglich sein.
  2. Wir geben Neuem/Unbekanntem weniger Chancen, weil wir schon genug damit zu tun haben, das Bekannte und Beliebte anzuklicken. Wenn wir hingegen gezahlt haben, tendieren wir dazu, auch mal das nicht unmittelbar Erfreuliche, Bekannte, Flauschige zu ertragen – und schließlich gar zu mögen und zu vertreten. Wir haben schließlich gezahlt, also nutzen wir auch die bezahlten Inhalte.
  3. Wir begünstigen die Abnahme der Qualität von Inhalten, weil die im Überfluss unter immer größeren Konkurrenzdruck geraten. Der könnte theoretisch zwar auch dazu führen, dass die durchschnittliche Qualität zunimmt – de facto scheint das aber nicht so zu sein. Überlebensfähiger scheint nicht inhaltlich Tiefgehendes zu sein, sondern schnelle Aufmerksamkeitsattraktionen und schnell Befriedigendes.
    Das bedeutet nicht, dass es keine “guten Inhalte” mehr gibt, aber sie sind schwerer als im Mangel zu finden.

Mein Fazit: Wir müssen mehr zahlen für Inhalte. Wieviel genau mehr und in welcher Form, weiß ich nicht. Dass aber Zahlungen nötig sind, scheint mir unausweichlich. Nur wenn wir zahlen, wenn Konsum insofern spürbar wird, kommen wir zu einem bewussteren Umgang mit unserer Zeit, mit unseren Ressourcen.

Welchen Schaden die Abwesenheit von Kosten anrichten kann, sehen wir jeden Tag an der wachsenden Spam-Flut (auch innerhalb von Unternehmen). Da Emails nicht kosten, gibt es kein Halten. Jeder verbreitet alles an alle, wenn ihm danach ist. Was ihm keine Mühe macht, belastet allerdings die Empfänger. Das ist umso schlimmer, da Email ein Push-Medium ist.

Ähnliches gilt für die Veröffentlichung in anderen Medien. Mit dem Internet ist es jedem jederzeit möglich auf jedem Niveau etwas zu veröffentlichen – sei es in Newsgroups, Blogkommentaren oder gar eigenen Blogs. Das kostet nichts beim Schreiben und nichts beim Lesen. Google bringt es in haltlos wachsender Menge vor meine Augen.

Wenn es aber noch vor dem Internet eine zivilisatorische Errungenschaft gab, dann ist es die Impulskontrolle. Sie ist sogar womöglich im Kern der Definition von Zivilisation. Wir sollten nicht jedem Impuls nachgeben. Warum preisen wir dann das Internet mit seiner Null-Kosten-Kultur so hoch? Ist das nicht merkwürdig. Dort leben wir jeden Impuls zur Äußerung und Konsumption einfach aus. Kost´ ja nix.

Zwar beschert uns das vorher ungekannte Freiheit und Datenfülle. Andererseits scheint es mir sowohl bei der Produktion wie der Rezeption Qualitätsmängeln Vorschub zu leisten.

Deshalb mein Gedanken, Produktion und Rezeption mit Geld zu steuern. Denn um nichts anderes geht es: Steuern von Zeitinvestitionen. Eine Latte, über die man springen muss, damit die knappe Ressource Zeit lohnenswert investiert ist.

Das ginge zwar auch irgendwie wohl mit mehr Selbstdisziplin und/oder Medienkompetenz. Vielleicht fehlt die mir ja schlicht als prä-Digital Native? Doch das gute alte Geld scheint mir ein einfacheres Mittel. Ich bin also im Allgemeinen dafür, mehr zu zahlen. Und ich bin auch bereit, selbst ganz konkret mehr zu zahlen und dadurch weniger Inhalte “at my fingertips” zu haben.

Dass das Bezahlen online anders als offline funktionieren muss, ist klar. Im Internet muss es Möglichkeiten zum kostenlosen Stöbern geben wie im Buchladen. Und Inhalte sollten in verschiedenen Granularitäten gekauft werden können. Am Ende tun wir uns aber, so glaube ich, alle einen Gefallen, wenn wir wieder für Inhalte zahlen.

Mehr, deutlich mehr kostenpflichtige Inhalte scheinen mir unausweichlich. Im historischen Rückblick werden deshalb wohl die ersten 15-20 Jahre “öffentliches Internet” mit seiner Null-Kosten-Kultur als Zeit der Verirrung angesehen werden. Schiere Datenflut ist eben nicht genug. Es braucht Hilfe beim Lenken und Fokussieren von Ressourcen. Und da ist Geld immer schon ein sehr probates Mittel gewesen.

Donnerstag, 8. Oktober 2009

Material zu meinen Vorträgen auf der ADC 2009

Die ADC 09 ist fast vorbei – nur noch morgen ein Workshop zum Thema Architektur. Hat wieder Spaß gemacht. Gute Gespräche, ein interessiertes Publikum, Sedgeway fahren :-) Und: die ersten CCD-Mausmatten haben den Weg in die Community gefunden.

Da ich in guter Tradition meine Vorträge ohne PPT-Folien gemacht habe, um den Aufmerksamkeitsfokus der Teilnehmer nicht vom Wesentlichen, dem Thema, auf irgendwelche Projektionen abzulenken, hier einige Hinweise für die, die sich damit weiter beschäftigen möchten:

  • Der Quellcode meiner CCR- und ApplicationSpace-Vorträge steht hier zum Download inkl. Microsoft CCR. Der CCR Space als Wrapper um die CCR ist ein Open Source Projekt bei Google: http://code.google.com/p/ccrspace. Der Application Space ist ebenfalls Open Source, aber bei CodePlex: http://xcoappspace.codeplex.com/
  • Die CCR ist Teil des Robotics Studio, dessen Nutzung einer Lizenz unterliegt. Wer die CCR produktiv einsetzen will – und das kann man tun, denn sie ist ausgereift –, sollte sich daher einmal anschauen, welche Lizenz passt. Mit einer MSDN Subscription “hat” man die CCR auch, wenn ich mich nicht irre; ansonsten kann man sie auch als sog. “CCR und DSS Toolkit” für kleines Geld kaufen.
  • Literatur zur CCR kann ich leider nicht so recht empfehlen. Es gibt versprengt im Netz einiges – aber das beleuchtet die CCR nur punktuell. Die CCR Doku ist nicht ausführlich. In der dotnetpro habe ich aber zwei Artikel im Jahr 2009 darüber geschrieben; die sind natürlich über das online Archiv hier und hier verfügbar.
  • Clean Code Developer ist ausführlich im Wiki beschriebe: www.clean-code-developer.de. Dort gibt es auch Hinweise auf Berichte über CCD inkl. Videos. Wer dann mitdiskutieren will, ist herzlich zu den Foren bei XING und Google eingeladen.
  • Über Agile Führung bzw. Soziokratie habe ich schon recht ausführlich hier im Blog geschrieben. Außerdem äußere ich gelegentlich Gedanken dazu in einem eigenen Blog: http://soziokratie.blogspot.com. Dort gibt es auch weitere Hinweise zu Quellen gleich in einem der ersten Beiträge. Und es gibt natürlich auch eine “offizielle Repräsentation” dieses Organisationskonzeptes; das ist in Deutschland das Soziokratische Zentrum: www.soziokratie.org.
  • Im Architekturworkshop ging es um die systematische Transformation von Anforderungen in wartbare “Grobstrukturen” und dann deren Übersetzung in konkreten Code. Das Softwareuniversum und Softwarezellen waren Thema. Dazu habe ich schon einiges über die Jahre geschrieben. Hier der Lesestoff in ziemlich chronologischer Reihenfolge:

Auf der ADC habe ich schon einige Fragen zu diesen Themen beantworten können. Doch es ergeben sich im Nachhinein bestimmt noch mehr. Bitte zögern Sie dann nicht, mich per Email (info (at) ralfw (dot) de) oder über die Kommentare hier im Blog zu kontaktieren. Ich bin gespannt auf Ihre Gedanken.

Donnerstag, 24. September 2009

Modelle für Geschäftsanwendungen mit Domain-Driven Design

Software soll man zuerst modellieren, heißt es. Nicht gleich loscodieren, sondern erst irgendwie planen. Dieses Planen bezeichnet man oft als Modellieren. Aber was bedeutet “Modellieren”? Was ist ein Modell für eine Software?

Was ist ein Modell?

Wikipedia sagt zum Begriff “Modell”:

“Von einem Modell spricht man oftmals als Gegenstand wissenschaftlicher Methodik und meint damit, dass eine zu untersuchende Realität durch bestimmte Erklärungsgrößen im Rahmen einer wissenschaftlich handhabbaren Theorie abgebildet wird. Da im Allgemeinen nicht alle Aspekte der untersuchten Realität in Modellen abbildbar sind, wird Modellbildung oftmals als Reduktion, Konstruktion oder Abstraktion bezeichnet.”

Achso, alles klar, oder? ;-) Nein, ich glaube, das verdient noch etwas Übersetzungsmühe. Die wichtigen Begriffe sind aus meiner Sicht:

  • Realität: Die Problemdomäne mit ihren Anforderungen
  • Theorie: Das Modell als eine mit Unsicherheiten/Unvollständigkeiten behaftete Beschreibung der Realität
  • Reduktion: Das Modell beschreibt nur einen Ausschnitt der Realität
  • Abstraktion: Das Modell beschreibt die Realität in allgemeinen Begriffen

Ein Modell beschreibt also einen Ausschnitt der Realität in allgemeinerer Form und unter bewusster Auslassung mancher Details. Modelle konzentrieren sich also auf das Wesentliche aus einem bestimmten Blickwinkel.

image Beispiel: Für die Problemdomäne “Schuss aus einer Kanone” ist die Formel y=x^2 ein Modell, das den Flug der Kanonenkugel beschreibt. Das Modell reduziert die Problemdomäne, weil in ihm z.B. Flugbahneinflüsse durch Wind nicht berücksichtigt werden; und es abstrahiert von den Konkreta Kanone, Pulver, Kugel usw., indem es die Flugbahn mit rein mathematischen Mitteln beschreibt. In der Formel gibt es keine Kanone und keine Kugel und kein Ziel mehr, sondern nur noch Punkte in einem Koordinatensystem.

Der Zweck solchen Modells ist es, eine Domäne in der Realität besser zu verstehen, um dann Antworten zu liefern. Ein Modell hilft also Fragen zu klären, die sich im Detailreichtum der Realität nicht so einfach beantworten lassen, z.B. die, wie eine Kanone ausgerichtet werden muss, um ein Ziel zu treffen.

image Modelle sind damit wie Landkarten. Sie schaffen Überblick. Mit ihnen wird die Planung einer Lösung handhabbar, ohne dass man sie sogleich ausführen muss.

Sie können sich von A nach B einen Weg durch ein Terrain bahnen. Das ist dann schon eine konkrete Lösung. Oder Sie können auf einer Landkarte mit dem Finger wandern. Da gewinnen Sie einen Überblick, erkennen Hindernisse, können Alternativen diskutieren. Im Gelände “implementieren” Sie dann Ihre Weg-Planung. Das geht zwar nicht glatt, aber die Wahrscheinlichkeit großer Überraschungen ist viel geringer als ohne Terrain-Modell. Die Landkarte hilft also bei Beantwortungen von Fragen an ein Terrain.

Gleiches gilt für das Modell eines Hauses. Früher war ein solches Modell materiell. Es sollte z.B. dem Auftraggeber einen Überblick über das spätere Erscheinungsbild geben. Der konnte sich das anhand von Blaupausen schlecht vorstellen. Also baute ein Modellbauer ein Modell zum “Sehen und Anfassen”. Vor der Ausführung konnten anhand des Modells dann Fragen beantwortet werden wie z.B. “Ist die Anordnung der Räume so praktikabel, wie man es sich gedacht hat?” oder “Pass sich das Haus gut in die Umgebung ein?”

Details der Realität wie Baustoffe oder Stabilität blendet so ein Modell aus, es reduziert die Realität. Und die Abstraktion besteht in der Handhabbarmachung. Das Modell ist kleiner als das spätere reale Haus. Es konzentriert sich auch auf das für die Fragestellungen Wesentliche, z.B. Größenverhältnisse oder Stil. Damit dient es einem einfacheren Erkenntnisgewinn als er möglich wäre, würde das Haus erst komplett ausgeführt.

Eine Kulisse ist deshalb kein Modell. Erstens dient sie ohnehin nicht dem Erkenntnisgewinn, zweitens macht sie die Realität nicht wirklich handhabbarer, weil sie Originalgröße hat. Da nützt auch die Reduktion nichts, die eine Kulisse betreibt. Sie besteht z.B. nicht aus dem Baustoff realer Häuser.

Dass dann heute Hausmodell meist virtuell sind, tut ihrem Modellcharakter keinen Abbruch. Im Gegenteil! Die Virtualität betont ihn. Sie reduzieren quasi maximal und können je nach Fragestellungen ganz unterschiedlich abstrahieren, z.B. indem sie sich auf das Aussehen oder die Wärmeleitung oder einen anderen Aspekt konzentrieren.

Modelle in der Softwareentwicklung

Modelle in der Softwareentwicklung müssen dieselben Kriterien wie Modelle in anderen Bereichen erfüllen, um wirklich Modelle zu sein. Sie müssen also reduzieren und abstrahieren und es damit einfacher machen, Fragen zu beantworten bzw. einen Überblick zu gewinnen.

Diese Modelle können sich auf die Problemdomäne beziehen und z.B. einfach nur die Realität des Kunden beschreiben. Dann dienen sie der Analyse. Oder sie können sich auf die Lösung beziehen, d.h. die Software, welche Anforderungen in der Problemdomäne des Kunden erfüllen soll. Dann dienen sie dem Entwurf. Wenn von Softwaremodellierung die Rede ist, sind Entwurfsmodelle gemeint. Darauf konzentriere ich mich denn auch im Folgenden.

Wie könnte nun ein Softwaremodell aussehen? Im Grunde ist alles recht, was reduziert und abstrahiert – und zwar von der Implementation! Ha!

Das müssen wir uns auf der Zunge zergehen lassen: Ein Entwurfsmodell abstrahiert von der Implementation. (Die Reduktion lasse ich mal unter den Tisch fallen, um die Argumentation einfacher zu halten.)

Klar, abstrahieren muss ein Modell, sonst bietet es keinen Vorteil gegenüber der Implementation. Oder anders: Modellierung findet nur dort statt, wo abstrahiert wird. Was nicht abstrahiert, das ist kein Modell.

Klassendiagramme modellieren nicht

Das lässt eigentlich nur eine sehr ernüchternde Erkenntnis zu: Die üblichen Klassendiagramme sind keine Modelle. Sie abstrahieren einfach nicht. Klassendiagramme sind nur eine andere Notation für das, was auch in Quellcode ausgedrückt werden kann – und zwar 1:1. Sie machen das zwar visuell, aber deshalb reduzieren sie allerhöchstens wenig (weil sie Methodenimplementationen auslassen). Vor allem abstrahieren sie dadurch jedoch nicht. Eine Klasse als Rechteck liegt auf keinem höheren Abstraktionsniveau als eine textuell beschriebene Klasse.

Wer direkt mit Klassen seine Software entwirft, der modelliert sie also nicht, sondern legt gleich sozusagen “Stein auf Stein”. Da macht es auch keinen Unterschied, wenn diese Klassen mit CRC Cards im Rollenspiel gefunden werden. Solange Klassen gesucht werden, findet keine Modellierung statt.

Das ist natürlich nicht per se falsch. Aber man sollte sich bewusst sein, dass man eben im Terrain wandert und nicht auf eine Karte blickt. Visualität macht noch kein Modell. Modell ist, was abstrahiert. Egal wie.

Etablierte Modelle der Softwareentwicklung

Nachdem ich nun die für manche wohl heilige Kuh “Klassendiagramm” geschlachtet habe ;-), stellt sich ganz berechtigt die Frage, was denn dann Modelle sein sollen.

imageEin Blick in die Informatikliteratur erhöht die Modellkenntnis, könnte man in Anlehnung an ein juristisches Sprichwort sagen. Da findet sich nämlich z.B. ein Titel wie dieser: “Modellierung – Grundlagen und formale Methoden”.

Darin – soviel sollte inzwischen gewiss sein – findet sich kein einziges Klassendiagramm. Modellierung hat einfach nichts mit einem Programmierparadigma wie der Objektorientierung zu tun. Ja, ich möchte fast sagen, sofern in einer Darstellung Spuren einer Plattform oder eines Implementationsparadigmas zu finden sind, kann es kein Modell sein. Das trifft allemal auf Klassen und ihre Verwandten die Module zu. Klingt rigoros und ist hier bewusst so gemeint. Sie werden sehen, worauf ich abziele.

Wenn kein Klassendiagramm, was aber dann als Modell? Unter anderem bietet das empfehlenswerte Büchlein diese Modellarten:

  • Graphen
  • Endliche Automaten
  • Grammatiken

Wenn Sie einen online Shop programmieren sollen, dann könnten Sie den z.B. als endlichen Automaten modellieren.

image

Das ist eine echte Abstraktion der angestrebten Lösung! So bekommt man Übersicht, so lassen sich Fragen leicht stellen. Das versteht womöglich sogar der Kunde, für den Sie den Shop entwickeln.

Die Visualität der Darstellung macht die Modellhaftigkeit allerdings nicht aus. Der Automat wäre immer noch ein Modell, würden Sie ihn als Tabelle beschreiben:

  Suchen In Warenkorb Zur Kasse Menge ändern Bezahlen
Z1 Z1 Z2      
Z2 Z2   Z3    
Z3       Z3 Z1


Warum nun aber ein Modell für den Shop und nicht einfach nur ein hübsches Klassendiagramm? Der Sprung wäre zu weit von der Problemdomäne direkt in die Lösungsdomäne. Sie würden quasi mühevoll einen Berg durchtunneln. Den Berg, der das Tal des Problems vom Tal der Lösung trennt.

Mit einem Modell dazwischen machen Sie eine Wanderung vom einen ins andere Tal. Sie arbeiten sich einen Abstraktionsberg hinauf, der Ihnen auf dem Gipfel eine schöne Aussicht auf beide Täler bietet. Zumindest beim Abstieg ins Lösungstal können Sie daher Ihren Weg planen, weil Sie vom hohen Abstraktionsgipfel auf die Niederungen des Lösungsterrains blicken. Außerdem übersehen Sie aber auch noch das Terrain Ihres Aufstiegs. Sie bekommen also auch einen besseren Überblick über das Problem.

In Bezug auf das Shop-Beispiel bedeutet das: Die herausgearbeiteten Ereignisse und Zustände machen dem Kunden klar, wie Sie seine Problemdomäne verstanden haben. Ihm geht jetzt womöglich auf, dass man nach dem Gang zur Kasse keinen weiteren Artikel mehr in den Warenkorb legen kann.

Und Sie haben eine sehr abstrakte Beschreibung Ihrer Lösung, die Sie einfach in Code umsetzen können. Für Zustandsautomaten gibt es etablierte Implementationsstrategien. Die können Sie natürlich als Klassendiagramme skizzieren. Die Ebene des Modells verlassen Sie jedoch damit.

Gleiches gilt, wenn Sie z.B. die Kommunikation zwischen Client und Server als Konversation in einer Sprache modellieren, die Sie mit einer Grammatik beschreiben.

Und es gilt auch, wenn Sie Ihren Ansatz zur Lösung der KataPotter als Graph modellieren.

Spüren Sie doch einmal in sich hinein: Fühlen Sie beim Umgang mit einem echten Modell nicht auch eine gewisse Leichtigkeit? Ist es nicht entspannend, sich nicht mit Klassen herumschlagen zu müssen? Oder wenn, dann sollten die Klassen sich quasi von allein ergeben aus dem Modell – als Implementierungsdetails nach einer Übersetzungsvorschrift. Das kann doch nur der Korrektheit Ihrer Lösung zuträglich sein.

Modelle für Geschäftsanwendungen

Jetzt zur Masterfrage: Wie sollen denn Modelle für Geschäftsanwendungen aussehen? Was, wenn Graphen, Automaten, Grammatiken nicht passen und auch keine griffige Formel Problem bzw. Lösung hübsch abstrakt beschreiben?

Nun, nicht triviale Geschäftsanwendungen bieten sicherlich Platz für mehrere Modelle. Die Benutzerinteraktion mag eben doch passend mit einem endlichen Automaten modelliert werden. Und für die Planung von Ressourcen bietet sich vielleicht ein mathematisches Modell an.

Doch die formalen Modelle aus Informatik und Mathematik decken sicherlich nicht die ganze Anwendung ab. Was also tun mit den Lücken? Oder was tun, um die verschiedenen kleineren Modelle unter einem Dach zusammenzufassen?

imageIch glaube, genau dafür macht ”Domain-Driven Design” (DDD) von Eric Evans einen Vorschlag. Der lautet: Modelliere (Geschäfts)Anwendungen mit Services, Entitäten bzw. Aggregaten und Value Objects sowie den “Hilfsdiensten” Applikationsschicht und Repository.

Dass es sich bei Services usw. um Elemente eines Modells handelt, können sie an der Abwesenheit von Implementationsdetails erkennen. Diese Konzepte haben nichts direkt zu tun mit Klassen. Sie können Service, Entität oder Repository auch mit C implementieren. Mit C# mag es leichter fallen, doch Objektorientierung ist nicht zwingend nötig.

Die dynamischen Aspekte eines online Shops mögen Sie nun also mit einem endlichen Automaten modellieren. Was ist aber mit den statischen Aspekten? Was fließt durch den Automaten? Wer sind die Beteiligten an einem Bestellvorgang?

Hier kommt DDD ins Spiel. Denken Sie nicht sofort in Klassen, sondern in DDD Begriffen. Für die gibt es später Übersetzungen in Klassen. Aber zunächst wollen Sie nichts von diesen Details wissen. Der online Shop lässt sich leichter auf einem höheren Abstraktionsniveau entwerfen.

Fragen Sie also: Was sind die Entitäten? Was sind die Value Objects? Was sind die Services?

Ohne hier näher auf DDD eingehen zu wollen, liegen folgende Elemente eines online-Shop-Modells nahe:

  • Warenkorb-Entität
  • Warenkorbposition-Value-Object
  • Produkt-Entität
  • Kunde-Entität
  • Warenkorb-Aggregat
  • Warenkorb-Repository
  • Produkt-Repository
  • Kunde-Repository
  • Bestellung-Service

Ähnliches hätten Sie auch gleich mit einem normalen Klassendiagramm entwerfen können? Irgendwie natürlich schon. Aber Sie hätten Ihr Denken damit nicht beschränkt. Sie hätten sich schnell in Details verloren. Zum Beispiel frisst “die Persistenzfrage” oft viele mentale Ressourcen.

Mit DDD gibt es “die Persistenzfrage” jedoch im Grunde nicht. DDD sagt: Wo Entitäten sind, da sind Repositories mit ganz einfacher Schnittstelle für die Persistenz zuständig. DDD abstrahiert also von den vielen möglichen Wegen, Daten zu persistieren.

Der Wert eines DDD-Modells liegt also in den Entscheidungen, die es schon für Sie getroffen hat. Wie werden Daten überhaupt zusammengefasst? Wie greift man auf Daten zu? Wie modifiziert man Date? Wie werden Daten persistiert? Darauf hat DDD einfache Antworten. Wer DDD betreibt, tut es dann halt so – und bekommt durch diese Beschränkung – oder besser: durch diese Fokussierung – Freiraum, sich um das Wesentliche der Problemlösung zu kümmern.

Wie endliche Automaten und Grammatiken und mathematische Formeln ist DDD natürlich keine “one size fits all” Modellart. Ob sich ein Game oder eine Maschinensteuerung damit geeignet modellieren lässt, mag bezweifelt werden. Oder vielleicht auch nicht. Denn die Modellelemente von DDD sind so allgemein, dass sie sich womöglich in allen nicht trivialen Anwendungen finden lassen.

Aber einerlei: Auch DDD hat selbstverständlich seine Grenzen.

Dennoch glaube ich, dass es sich lohnt, beim Entwurf von Software Ausschau zu halten nach Anwendungsmöglichkeiten für DDD. DDD bietet ein unaufdringliches Metamodell mit einfach zu verstehenden Elementen. So hilft DDD, Software echt zu modellieren, d.h. sich nicht in Implementationsdetails zu verlieren.