Sonntag, 26. Juni 2011

Tägliche Codeproduktionsquote

Wieviel Code produzieren Sie eigentlich? Wieviele LOC pro Tag, wieviele LOC davon Produktionscode? Schätzen Sie mal. Sind das 100, 500, 1000 Zeilen (ohne Kommentare, automatisch generierte using-Anweisungen und ohne Tests)?

imageHabe gerade die Code-Review-Fibel von SmartBear gelesen. Darin ging es auch um LOC.

Empfohlen wird nämlich, pro Peer Code Review nicht mehr als 200+ LOC durchzusehen. Da frage ich mich, wie lang ein Entwickler an diesen durchzusehenden Zeilen gesessen haben mag.

Der Review sollte nicht länger als 60-90 Minuten dauern. Wieviel Zeit ist vorher aber in die Produktion der Sourcen geflossen? Weniger oder deutlich mehr?

Keine Ahnung. Aber ich rechne einfach mal:

Wenn 1 Entwickler pro Tag 200 LOC netto Produktionscode schreibt, dann kommen pro Woche 1.000 LOC heraus und bei 200 Arbeitstagen pro Jahr 40.000 LOC. Ein Team von 3 Entwicklern käme auf 120.000 LOC/Jahr, 5 Entwickler auf 200.000 LOC/Jahr.

Das finde ich nicht schlecht. Denn insgesamt sind das ja immer doppelt soviele LOC, weil Produktionscode natürlich testgestützt ist und Tests gut und gern 50% des Gesamtcodeumfangs ausmachen.

120.000 LOC oder 200.000 LOC… das sind keine kleinen Projekte. Dazu kommt, dass eine Codeproduktionsquote von 200 LOC/Tag mit der Zeit immer mehr Kundenwert produziert. Je mehr Code schon existiert, desto eher können 200 neue Zeilen auf schon geschaffene Abstraktionen zurückgreifen.

200 LOC/Tag pro Entwickler mögen sich wenig anhören. Und natürlich kann man mehr “raushauen”, wenn man ein Code-Cowboy ist. Doch die 200 LOC/Tag, die ich meine, sind sauberer, d.h. verständlicher, evolvierbarer und gut testabgedeckter Code. Solche 200 LOC/Tag pro Entwickler konsequent produzieren, finde ich daher völlig akzeptabel.

image

Dabei fällt mir die Clean Code Developer School ein, deren 5. Staffel wir vor ein paar Wochen abgeschlossen haben. Darin unterrichten wir den Stoff an vielen Projekten (AppKata würde ich sie heute nennen). Und jedes Projekt durchlaufen wir nach einem systematischen Prozess, zu dem mindestens gehören: Anforderungsanalyse, Feature Slicing, Modellierung, Implementierung, Code Review.

Pro solcher Iteration produziert jeder Teilnehmer ca. 50-80  LOC Produktionscode, schätze ich mal. Und pro Tag durchlaufen wir meistens 2 Iterationen. D.h. unter Lernbedingungen, wo alles etwas langsamer geht, kommen schon 100-150 LOC raus.

imageDer Review dauert je nach Übung 30-60 Minuten. Dabei schauen wir den Code von 1 bis 3 Teilnehmern unter verschiedenen Gesichtspunkten an.

Und am Ende des Tages? Da sind wir zufrieden. Das finde ich sehr wichtig. Wir wissen einfach, dass wir Qualität produziert haben. Wir schließen immer mit einer Retrospektive und reflektieren unsere Arbeitsweise.

Wenn ich das nun auf ein Projekt übertrage, dann scheinen mir 200 LOC/Entwickler netto Produktionscode als Tagesquote ein brauchbarer Wert.1 Der Code lässt sich dann nämlich nicht nur schreiben, sondern auch noch in einem Code Review lesen.

Der Rhythmus könnte z.B. so sein: Heute 200 LOC produzieren, morgen die 200 LOC annotieren (d.h. als Produzent reflektieren, s. Code Review Fibel) und Peer Code Review durchführen. Dann hat man einmal drüber geschlafen und schaut seinen Code am nächsten Tag frischer an. Das trägt sicher positiv zur Fehlerfindung bei. Was heute geschrieben wurde, ist morgen durchgesehen und bereit für die QS. Jeden Tag wieder. Konsequent. Systematisch.

Zuwenig Code sind 200 LOC/Tag/Entwickler also nicht.

Aber sind sie vielleicht zuviel? Ja, in manchen Projekten mögen Entwickler nicht einmal auf soviele LOC/Tag kommen. Immer ist irgendwas, das sie davon abhält: Meetings, Support, Dokumentation schreiben… Die Ablenkung lauert überall.

Sollte das der Fall sein, dann haben Sie nun mit den 200 LOC Codeproduktionsquote für den Tag einen Wert, über den Sie mit dem Chef reden können. Solange die “Produktionsverhältnisse” nicht so sind, dass Sie jeden Tag 200 LOC netto Produktionscode schreiben, solange müssen die Verhältnisse noch verbessert werden. Aber Achtung: Dazu gehört natürlich auch der tägliche Code Review!

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat…

Fußnoten

1 Sollten Sie mehr Code “raushauen” können pro Tag, dann müssen Sie sich fragen, wann dafür Code Review stattfinden soll. Ich bezweifle, dass Sie bei einer höheren Produktionsquote systematisch mit Code Reviews die Qualität steigern können.

Samstag, 25. Juni 2011

Porno statt Erotik in der Programmierung

Die brand eins titelt gerade mit “Das Erotische setzt das Geheimnis voraus. Wo es ganz verschwindet, beginnt die Pornografie.” (Byung-Chul Han, Ausgabe 7/2011)

image

Das bezieht sich im Heft auf das gesellschaftliche Top-Thema Transparenz. Aber mich hat der Ausspruch zum Nachdenken über Softwareentwicklung angeregt. Die kommt mir in der Praxis vieler Projekte nämlich wie die Kunst der Verhüllung vor: wie Erotik.

Zwei aktuelle Beispiele ganz unterschiedlicher Art: Zuerst ein Codeausschnitt aus der dotnetpro 7/2011:1

image

Ein, wie ich finde, sehr erotisches Stück Code. Der Autor verhüllt mit Geschick die Funktionsweise. Was tut der Code? Wie tut der Code das? Sprachkonstrukte, Whitespace und Namensgebung kommen kunstvoll zum Einsatz zur Komposition eines geheimnisvollen Ganzen.2

Ganz anders und doch ebenfalls hocherotisch dieser Code aus einem Blog:3

image

Hier ist die Intention zwar viel deutlicher – doch das kompensiert der Autor mit technologischer Rafinesse. Ein Meister des lambda-Schleiertanzes legt er alles in wenigen Zeilen offen – und verwehrt dem Leser doch einen schnellen Durchblick. Das Verständnis der Lösung, warum sie tatsächlich tut, was sie verspricht, will erarbeitet sein. Eleganz als höhere Form der Erotik.

Doch während Erotik in der Kunst einen hohen Stellenwert hat und von Geschmack zeugt, da ziehe ich für den Broterwerb durch Softwareentwicklung weniger davon vor.

“Auf der Arbeit” brauche ich kein Geheimnis. Geheimnis – gewollt oder ungewollt – ist kontraproduktiv. Geheimnisse machen Mühe, wie jeder weiß, der Krimis liest. Es dauert, bis man sie entschlüsselt hat. Und am Ende ist man womöglich gar nicht sicher, ob man das vollständig geschafft hat.

Wenn Erotik für Geheimnis steht und Porno für Geheimnislosigkeit, Unverhülltheit, Klarheit, Direktheit, dann bin ich für mehr Porno in der Softwareentwicklung. Gerne Erotik, wenn ich Zeit dafür habe. Gerne auch “höhere Erotik”: Eleganz. Im Zweifelsfall jedoch bitte krasse Nackheit.

Ich will mit Code umgehen, der sich nicht verhüllt. Enthüllen ist etwas für Journalisten. Auspacken ist etwas für Weihnachten. Geheimnisse lüften überlasse ich gern den Drei Fragezeichen.

Code hingegen soll alles zeigen. Da will ich schnellstmöglich zur Sache kommen. Programmiervorspiel, nein danke!

Ich mag pornografischen Code. Er soll zum Beispiel “intention revealing” sein:

A design in which the names of classes, methods, and other elements convey both the original developer's purpose in creating them and their value to a client developer.

Und Code soll sich mir attraktiv aus unterschiedlichen Entfernungen darstellen. Aus der Ferne soll er eine wohlgestalte Erscheinung abgeben. Bin ich näher an ihm dran, zeigt er mir mehr Details, die mir Spaß machen. Bis ich zum Schluss ganz nah in der IDE handgreiflich werde, um seine Strukturen leicht an neue Anforderungen anzupassen.

In der Erotik zählt Geschwindigkeit nicht. Da steht die Kunst im Vordergrund.

In der täglichen Arbeit jedoch, halte ich es mit der Königin in Hamlet (2. Akt, 2. Szene):

POLONIUS
So wäre dies Geschäft nun wohl vollbracht.
Mein Fürst und gnädge Frau, hier zu erörtern,
Was Majestät ist, was Ergebenheit,
Warum Tag Tag; Nacht Nacht; die Zeit die Zeit:
Das hieße, Nacht und Tag und Zeit verschwenden.
Weil Kürze denn des Witzes Seele ist,
Weitschweifigkeit der Leib und äußre Zierat:
Faß ich mich kurz. Eur edler Sohn ist toll,
Toll nenn ichs: denn worin besteht die Tollheit,
Als daß man gar nichts anders ist als toll?
Doch das mag sein.

KÖNIGIN
                    Mehr Inhalt, weniger Kunst!

Ich wünsche mir mehr Inhalt, Inhalt in Form klarer Funktionsweise, klarer Struktur.

Denn solange nicht nackte Klarheit unser Hauptziel ist bei der Codierung, haben wir ein Verständnisproblem. Kompaktheit oder Eleganz sind zweitrangig. Was zählt, sind Effizienz und Effektivität bei der Lektüre von Code.

Also: Mehr Porno, weniger Erotik in der Programmierung.

Fußnoten

1 Wenn Sie den Code auch in der Vergrößerung nicht entziffern können, macht das nichts. Er würde auch in Originalgröße sein Geheimnis nicht leicht preisgeben. Ich habe deshalb eine bidlich längere Darstellung einer besser lesbaren vorgezogen.

2 Tut mir leid, Andreas, ich kann mir die Ironie angesichts deines Codes nicht verkneifen. Bitte nimm es nicht persönlich. Ich spendiere ein Bier, wenn wir uns mal wieder sehen.

3 Sergej, auch für dich ein Bier bei der nächsten Begegnung.

Donnerstag, 23. Juni 2011

Why UML got it wrong - Abstraktion vs Modell

Jetzt hab ich es endlich, was mich an der UML stört. Es ist nicht mal ihre Aufgeblasenheit, sondern ihr falsches Versprechen. Die UML verspricht eine Vereinheitlichung von Modellierungsansätzen. Doch leider ist viel weniger Modellierung drin als suggeriert, wo Modellierung so fett gedruckt drauf steht.1

Dazu muss ich aber erklären, wie ich Modellierung verstehe – und was die UML stattdessen bietet.

Abstraktion

Bevor ich zu Begriff Modell komme, zunächst etwas über einen anderen Begriff, der etwas mehr Aufmerksamkeit verdient: Abstraktion. Wikipedia sagt, Abstraktion sei der “induktive[] Denkprozess des Weglassens von Einzelheiten und des Überführens auf etwas Allgemeineres oder Einfacheres”.

Abstraktion hat also mit Detaillierungsgrad zu tun. Wenn die Darstellung von etwas einmal mehr und einmal weniger Details enthält, dann ist sie einmal weniger und einmal mehr abstrakt. Das Gegenteil von abstrakt/allgemein ist also detailreich/konkret.

Einige Beispiele für abstrakt(er) vs konkret(er):

imageimage

imageimage

imageimage

image image

Darstellungen auf unterschiedlichem Abstraktionsniveau haben also mehr oder weniger Details. Dazu kommt allerdings noch eine vermeintliche Feinheit: Bei der Abstraktion bleibt man in der Domäne. Im ersten Beispiel (Karte) ist in der abstrakten wie in der konkreten Darstellung bei allem Unterschied im Detailreichtum von denselben Dingen die Rede; es geht um Straßen, Gewässer, Häuser usw. Dito beim letzten Beispiel: auch in der sehr abstrakten Schemazeichnung des Motors geht es um dieselben Dinge wie Zylinder, Kurbelwelle, Zündkerzen usw. wie beim konkreten Motor.

Bei gegebener Domäne stellen wir mit dem Abstraktionsregler also “nur” den Detailreichtum einer Darstellung ein. Manchmal ist das einfach, wie bei Karte oder Schemazeichnung. Manchmal schwieriger wie beim Artenbaum (Beispiel 2); Pflanzenarten sind Verallgemeinerungen von Merkmalsmengen. Die mussten erstmal gefunden werden. Von einer konkreten Straße auf eine bunte Linie zu abstrahieren, ist einfacher, als von tausenden Pflanzen auf ihre Arten.

Am Ende ist der Artenbaum jedoch auch “nur” eine Abstraktion. In ihm geht es um Pflanzen wie bei der Betrachtung einer einzelnen Rose.

Modell

Bisher bin ich mit dem Begriff Modell zu leichtfertig umgegangen. Als Beispiel habe ich oft eine Karte herangezogen. Das ist genau genommen aber falsch. Darum geht es mir in diesem Artikel.

Modelle sind auch detailreduzierte Darstellungen von etwas Konkretem. Doch sie sind anders als Abstraktionen, weshalb sie einen anderen Begriff verdienen. Modelle verlassen zur Vereinfachung der Darstellung die Domäne des Beschriebenen. Das “Material”, aus dem Modelldarstellungen sind, ist sozusagen ein anderes als bei Abstraktionen.

In der Abstraktion einer Stadt kommen Straßen, Gebäude, Wasserflächen vor; in einem Modell einer Stadt wäre das nicht so. In der Abstraktion eines Motors kommen Zylinder und Kurbelwelle vor, in einem Modell eines Motors nicht.2

Abstraktionen dienen der Verständlichmachung, dem Überblick. Die Vielfalt der Details wird reduziert, damit das Konkrete sozusagen in unseren Kopf passt. Wenn das reicht, dann ist es ok. Dann arbeiten wir mit Abstraktionen, um konkrete Problemlösungen zu entwerfen. Abstraktionen sind ein altes Mittel der Planung. Allemal Karten gibt es schon viele Jahrtausende.

Was aber, wenn die Abstraktion nicht reicht? Dann muss ein anderes Verallgemeinerungsmittel her, ein Modell. Natürlich beschreibt ein Modell wieder etwas Konkretes – doch mit anderen Mitteln. Hier einige Beispiele für Modelle:

imageimage

imageimage

imageimage

Dem Modell von einem Pendel ist nichts “Pendelhaftes” anzusehen, dem Zustandsautomaten ist nichts “Maschinenmäßiges” anzusehen; und selbst das neuronale Netz ist keine Abstraktion eines realen neuronalen Netzes, sondern ein Modell, weil darin keine Neuronen mehr vorkommen.

Modelle bilden das Modellierte natürlich ab, sie enthalten eine Essenz daraus. Modelle sind also an das Konkrete gebunden. Sie erfüllen darauf bezogen ja einen Zweck. Deshalb ist irgendwie natürlich das Konkrete auch im Modell vertreten. Allerdings nicht einfach durch Reduktion des Detailgrades, also eine veränderte Quantität, sondern durch eher durch Analogie, also eine andere Qualität.

Die Abstraktion Karte hat den Anspruch, die Realität zu sein – nur nicht komplett. Ein Modell hingegen hat nur den Anspruch wie ein Ausschnitt der Realität zu funktionieren oder ihn zu erklären.

Das wird deutlich, wenn ich ein Modell einer Abstraktion gegenüberstelle:

imageimage

Das Konkrete ist Deutschland mit seinem ganzen Leben und Weben. Das ist natürlich hier nicht darstellbar, also abstrahiere ich mit einer Karte. Das Organigramm für die Verwaltungsstruktur der Luftfahrt bezieht sich auf einen winzigen Ausschnitt des Konkreten – allerdings nicht mehr mit Mitteln der Abstraktion. In den Kasten “Deutscher Wetterdienst” kann man nicht hineinzoomen, da kommen nicht irgendwann Gebäude und dann der Deutsche Wetterdienst in de Blick. Es gibt nicht einmal einen konkreten Ort im Gewusel Deutschlands, an dem der Wetterdienst einzig existiert. Das Organigramm ist daher “nur” ein Modell, es erklärt eine Facette von Deutschland.

UML und die Modellierung

Mit diesen Definitionen von Abstraktion und Modell in der Hand ein Blick auf die UML 2.0. Sie definiert eine ganze Reihe von Diagrammen für die sich zu prüfen lohnt, ob sie tatsächlich etwas mit Modellierung zu tun haben.

Dazu ist natürlich erst einmal festzustellen, was denn modelliert werden soll. Was ist das Konkrete? Denn zwischen Modell und Abstraktion kann nur in Bezug auf Konkretes unterschieden werden.

Für mich als Softwareentwickler ist das Konkrete Quellcode und all die Artefakte, die ich sonst noch erzeuge (z.B. Datenbank, Datendatei, Assembly).

Wie steht es mit den UML Diagrammtypen in dieser Hinsicht?

Diagrammtyp Einsatzgebiet
Anwendungsfälle Modell
Klassen und Objekte Abstraktion
Beziehungen Abstraktion/Modell
Pakete Abstraktion
Deployment Abstraktion
Komponenten Abstraktion
Zustandsdiagramm Modell
Kommunikationsdiagramm Abstraktion
Timingdiagramm Abstraktion
Aktivitätsdiagramm Abstraktion
Interaktionsübersicht Abstraktion
Sequenzdiagramm Abstraktion

Hm… das sind nicht viele modellierungsrelevante Diagrammtypen in der UML. Sie sollte vielleicht besser UAL heißen: Unified Abstraction Language ;-)

Achtung: Das ist kein Qualitätsurteil. Abstraktion ist sehr nützlich. Die abstraktionsrelevanten Diagrammtypen haben ihren Wert. Nur sollte man sie eben nicht mit Modellierungswerkzeugen verwechseln.

Wenn das Konkrete Klassen, Methoden, Objekte, Objektreferenzen etc. sind, dann sind alle Diagramme, in denen Klassen, Methoden, Objekte, Objektreferenzen etc. vorkommen – egal in welchem Detaillierungsgrad – “nur” Abstraktionen.

Auch nett, nur eben keine Modelle. Ihnen fehlt also etwas, das wir in Modellen suchen.

Warum Modelle so wichtig sind

UML ist deshalb für mich knapp daneben, weil sowenig “echte” Modellierung drin steckt. Schön, dass wir Code damit abstrakter aus verschiedenen Blickwinkeln darstellen können – aber um Kompliziertes, nein, Komplexes entwerfen und darstellen zu können, ist mir das zuwenig.

Abstraktionen bieten mir schlicht, ähm, zuwenig Abstraktion :-) Bei Abstraktionen bin ich immer gezwungen, in der Domäne des Konkreten zu bleiben. Das bedeutet, ich muss mich ewig mit Klasse, Methoden, Objekte, Assemblies herumschlagen.

Wenn die damit sonst in der Welt zufrieden gewesen wären, hätten wir immer noch einen technischen Stand auf dem des Barock. Mit der Mathematik als universeller Modellierungssprache jedoch… damit konnten wir im wahrsten Sinn des Wortes abheben.

Und dasselbe ist uns auch schon in der Softwareentwicklung gelungen. SQL und reguläre Ausdrücke sind aus meiner Sicht Beispiele dafür, oder Prolog, neuronale Netze, Zustandsautomaten. Mathematische Ansätze und DSLs haben uns in einigen Bereichen sehr voran gebracht. Heute kann jeder einen Compiler mit ANTLR zusammenbasteln dank Zustandsautomaten und EBNF. Und auch die Abfrage komplizierter Datenbestände ist mir SQL kein Hexenwerk.

Für die meisten Softwareproblemstellungen gibt es jedoch kein allgemein anerkanntes Modellierungsverfahren. Und damit meine ich nicht einmal, dass Code soweit modelliert werden soll, dass man am Ende keine Zeile C# mehr schreiben muss.

Schade. Denn Modellierung spart viel Zeit. Was man nicht modelliert, das muss man sofort konkret umsetzen. (Naja, ein bisschen Hilfe durch eine Abstraktion kann auch noch dabei sein.) Am schwierigsten ist es aber immer, das Konkrete richtig hinzukriegen.

Zwar zählt am Ende nur das Konkrete, der funktionierende Motor, die laufende Software. Aber wenn man immer nur auf dem Konkreten rumrutscht, sind Veränderungen mühsam. Das ist, als hätte man keine Karte und bewegt sich in unbekanntem Terrain. Das geht – aber es ist mühsam.

Feldherren sitzen daher traditionell erhöht oder haben heute Satellitenunterstützung. Im konkreten Schlachtengetümmel lassen sich einfach manche Entscheidungen nur schlecht treffen.

Abstraktionen bieten, wie gesagt, mir nicht genug. Sie fesseln mich zu sehr an das Konkrete. Wenn ich eine Softwarelösung plane, will ich so lange wie möglich nicht über Details wie Klassen oder technologische Feinheiten wie WCF nachdenken. Nicht “gar nicht”, sondern nur “solange wie möglich nicht”.

UML erinnert mich einfach früher als hilfreich immer wieder an Details. Deshalb sind mir UML-Abstraktionen zu wenig; ich will echte Modelle.

Wie könnte es anders sein – der aufmerksame Leser meines Blogs wird es sich schon gedacht haben ;-) - scheint mir nun Flow-Design solche Modellierung zu ermöglichen. Denn damit kann ich Funktionalität beschreiben, ohne an Codekonkretheit denken zu müssen. In einem Flow-Design Modell tauchen keine Klassen auf. Die Übersetzung in Code ist ein separater Schritt. Darin gleicht Flow-Design SQL oder Zustandsdiagrammen.

Dass ein Flow-Design kein vollständiges Programm beschreibt, sondern nur einen Grobentwurf darstellt, ist dabei kein Nachteil, sondern eine Tugend. Modellierung soll Codierung nicht ersetzen, sondern sie erleichtern. Sprache wie C# sind zur Beschreibung mancher Lösungsaspekte gut geeignet, für andere nicht. (Wer anders denkt, sieht in 3GLs schon eine Silberkugel.) Also scheint es mir konsequent, C#, F#, VB, Java, C++ usw. zu komplementieren. Abstraktionen sind dafür aber nicht geeignet, weil sie ja die Domäne der Sprachen nicht verlassen. Also müssen Modelle her.

Die Frage ist für mich deshalb nicht, ob wir zu unseren 3GLs noch Modelle brauchen, sondern welche. Flow-Design ist mal ein Vorschlag, der für mich und viele andere vielversprechend ist.

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat…

PS: “Why UML got it wrong” schießt natürlich über das Ziel hinaus. Aber “Why UML is not enough” klingt nicht so knackig ;-) Für etwas mehr Aufmerksamkeit habe ich mir daher eine polemische Vereinfachung erlaubt.

Fußnoten

1 Ob die UML überhaupt eine Sprache ist (“”) oder nicht vielmehr ein Konglomerat an Sprachen, ließe sich auch diskutieren. Ebenso, ob es überhaupt sinnvoll ist, graphische Beschreibungsmittel für so unterschiedliche Aufgaben wie Spezifikation, Konstruktion und Dokumentation unter einem Dach zusammenzufassen. Aber davon vielleicht ein andermal.

2 Hier wird deutlich, dass ich offensichtlich etwas anderes mit Modell meine als ein Modellbauer. Wer das Modell eines Motors zusammenbastelt, hält natürlich Zylinder und Kurbelwelle in der Hand. An dieser Stelle würde ich einen Modellbausatz für einen Motor deshalb nicht Modell nennen, sondern Abstraktion. Wer mit Kleber und Farbe an einem Motor aus Plastik im Maßstab 1:10 bastelt, der hat einen “Abstraktionsbausatz” vor sich.

Flow-Design Motivation - Ein XING-Auszug

Warum ist Flow-Design eigentlich so gegen den üblichen Programmierstrich gebürstet? Dazu habe ich anlässlich einer Diskussion im CCD XING-Forum etwas geschrieben, das ich auch hier für mitteilenswert halte.

Anlass war ein Diskussionsbeitrag von Michael van Fondern:

Ich habe allerdings doch noch eine Frage, die du mir sicher beantworten kannst. Ich bin bisher bei der AppKata im Wesentlichen so vorgegangen

1. Datenabstraktionen bilden
(z.B. in Iteration 1: CsvDataTable/CsvDataRecord als Abstraktion für eine Tabelle)
Einfache Operationen, die sich unmittelbar auf diesen Daten ausführen lassen, die aber ansonsten keine spezielle Sicht implizieren, habe ich direkt bei diesen Klassen platziert (z.B. die CSV-Zerlegung direkt im Konstruktor des CsvDataRecord, oder die Spaltenbreitenberechnung).

2. Abstraktionen für einzelne Prozessschritte bilden (die zudem auf den Daten aus Schritt 1 arbeiten)
- als groben Prozessschritt habe ich die z.B. formatierte Sicht auf eine bestimmte Seite der Tabelle identifiziert und daraus eine Klasse gemacht; die einzelnen Teilschritte spielen sich dann als Funktionen in dieser Klasse ab. Wenn ich bei der Entwicklung / Weiterentwicklung irgendwann merke dass bei einzelnen Funktionen das SRP-Prinzip oder das SLA-Prinzip verletzt werden, refaktorisiere ich diese aus, platziere diese entweder in der Klasse selbst, oder bei einer Datenklasse, oder bilde ggf. auch neue Klassen dafür.

Meine Frage in Bezug auf EBC: was ist mit "Schritt 1 - Datenabstraktionen bilden" (auch in Form von "Objekten" der klassischen Objektorientierung, und ggf. unter Zuhilfenahme von Klassendiagrammen). Diesen Modellierungsschritt habe ich in deinen Artikeln, die du über EBC geschrieben hast, bislang nicht mehr gesehen, aber der ist doch immer noch sinnvoll, oder? Gerade, weil EB-Komponenten nur Nachrichten mit genau einem Parameter verschicken / empfangen. Oder hab ich das was nicht verstanden?

Die beiden Schritte, die er zur Lösung der CSV Viewer AppKata getan hat, scheinen mir typisch. Deshalb hier meine Antwort in Gänze (mit einigen Hervorherbungen und Bonusbildern):

Gut, dass du fragst :-) Du hast einen zentralen Punkt von Flow-Design identifiziert.

Dein Vorgehen:
1. Identifiziere Daten und unmittelbar auf ihnen ansiedelbare Operationen
2. Identifiziere sonstige Operationen

Das entspricht dem üblichen Vorgehen, würde ich sagen. So lehrt es die traditionelle Objektorientierung. Sie fokussiert auf deinem Schritt 1 - Schritt 2 ist eher ein Anhängsel, ein notwendiges Übel.

Objektorientierung traditionell bedeutet für mich Fokus auf Daten und Zuordnung von Funktionalität zu diesen Daten. Das Ergebnis sind Klassen als Blaupausen für Objekte.

Flow-Design stellt das auf den Kopf. Mit voller Absicht. Weil der OO-Ansatz zu den Ergebnissen geführt hat, die wir heute allerorten sehen.

Das Missverständnis des OO-Ansatzes ist es, dass Software soetwas ist wie eine Maschine. Maschinen bestehen aus Teilen, Kolben, Zündkerzen, Lichtmaschinen, Transistoren, ICs, Widerständen, Zeilentransformatoren, Tastaturen usw. usf.
Wenn man eine Maschine bauen will, dann überlegt man sich, wie die Bauteile aussehen sollen. Man denkt in distinkten Funktionseinheiten, die Zustand haben und mehr oder weniger tun. Eher mehr. Allemal bei Software, da man dort quasi immer bei Null anfängt.

Ein Elektrotechniker hat es einfacher: Der sitzt vor einem Kasten mit Standardbausteinen, die er "nur noch" zu etwas Neuem "verrühren" muss.

Softwareentwicklung kennt solche Standardbausteine im Grunde nicht (lassen wir ein paar Bibliotheken und Steuerelemente mal außen vor). Jedes Projekt erfindet sie daher neu in Form von Objekten. Dabei schießt man schnell über das Ziel hinaus. Die Standardbausteine sind keine Standardbausteine, weil sie einfach so groß werden. Deshalb immer wieder das Gejammer über mangelnde Reusability. Man möchte in die Position eines Elektrotechnikers kommen.

Wenn wir uns aber von dem Missverständnis verabschieden, dass Software eine Maschine ist, dann wird alles leichter. Es ist müßig, nach "Standardbausteinen" zu suchen. Der Setzkasten ist leer. Unsere Aufgabe sollte nicht sein, ihn erst zu füllen und dann mal zu schauen, was wir mit unseren eigenen Bausteinen bauen können.

Also geben wir den Fokus auf Datenstrukturen mit Funktionalitätsanhängseln auf. Weg mit dem Objektfokus. Weg mit dem Bauteildenken. (Dass auch im Flow-Design noch von Platinen und Bauteilen die Rede ist, ist ein Fehler, der korrigiert werden wird.)

Flow-Design hat ein anderes Softwarebild. Für FD ist Software keine Maschine, sondern eine Ansammlung von Prozessen, oder - weil der Begriff Prozess schon so besetzt ist - eine Ansammlung von Verhaltensweisen.

Bei FD beginnst du deshalb mit der Identifikation von Verhaltensweisen statt Daten, mit Verben statt Substantiven.

Was soll ein CSV Viewer leisten? Er soll eine CSV Datei seitenweise anzeigen.
Irgendwie werden also mal ganz grundsätzlich Textzeilen eines bestimmten Formats in Seiten eines bestimmten Formats transformiert.

Für denn OOPler stecken da natürlich hübsche Daten drin: Textdatei, Zeilen, Seiten. An die hängt er geistig schnell die ablesbaren Funktionen: lesen, formatieren.

Aber da beginnt schon das große Rätselraten: Wozu soll denn eine Funktionalität wie das Auseinandernehmen eines CSV Textzeile gehören? Ist das eine Aufgabe des Textdateiadapterobjektes? Es liefert CSV-Datensätze zurück, die aus Spaltenwerten bestehen? Oder soll sich der Adapter darauf beschränken, Textzeilen zu liefern und die Formatierung bricht sie auf? Hm...

FD ist da viel pragmatischer, direkter, natürlicher. Man fängt einfach mal an, die obige Anforderung zu formalisieren:

(run) -> (Dateiname von Kommandozeile holen)
      -(dateiname)-> (Erste Seite aus CSV Datei lesen)
      -(seite)-> [Seite anzeigen].

image

Damit ist ein Programm beschrieben, das schonmal ein Feature der ersten Iteration realisiert. Das Abstraktionsniveau ist sehr hoch, klar, aber das ist ja gerade der Trick. Wir haben das Programm formulieren können, obwohl wir nur eine grobe Ahnung von der Lösung haben.

Die Anforderungen sagen mir vor allem, was zu tun (!) ist. Welche Transformation erwartet der Benutzer? Denn um Transformationen geht es immer. Input wird in Output transformiert. So ist das Softwareleben. Immer. Unzweifelhaft.

Essenziell dreht sich Software damit um Funktion und nicht Daten. In der Mitte von EVA stehen nicht Daten, sondern Transformation.

Natürlich, ohne Daten geht es nicht. Aber wir dürfen uns nicht ins Bockshorn jagen lassen, nur weil Daten am Anfang und am Ende von EVA stehen. Die Daten sind nicht der Grund, warum wir V entwickeln sollen. Die Daten sind vielmehr schon da. Die Vorstellung davon, wie Input und Output aussehen, ist selbst beim Kunden verhältnismäßig klar. Er mag die nicht formalisieren können, aber er hat Daten und will andere Daten bekommen, von denen er weiß, wie sie aussehen sollen. Sonst hätte er nicht den Wunsch nach einer Software, der ihm genau diese Daten erzeugt.

Das, was dem Kunden aber viel, viel unklarer ist (und uns erstmal auch), das ist, wie die Transformation aussieht. Wie macht man das, den Input in den Output zu überführen? Das (!) herauszufinden, ist unsere Aufgabe.

V ist also unklar und wird nicht klarer, indem wir mit OOP lange über E und A grübeln. Wir müssen sofort, wenn wir Anforderungen sehen, V in den Blick nehmen. Zunächst ganz grob, dann immer detaillierter, am Ende indem wir Code schreiben.

Obiger Fluss ist ein grobes V für ein Feature des CSV Viewers. Noch gröber, aber recht uninteressant wäre:

(run) -> (Lade und zeige die erste Seite der CSV Datei an).

image

Im Zweifelsfall kannst du aber gern so beginnen. Dann wäre die Schrittfolge:

1. (run) -> (Lade und zeige die erste Seite der CSV Datei an).

image

2. (run) -> (Dateiname von Kommandoleise holen)
        -(dateiname)-> (Erste Seite aus CSV Datei lesen)
        -(seite)-> [Seite anzeigen].


image

und vielleicht folgende Verfeinerungen:

3.1 Erste Seite aus CSV Datei lesen {
    (in) -(dateiname)-> (Lese Textdatei zeilenweise)
         -(string*)-> (Zerlege CSV Textzeilen in Werte)
         -(CSVRecord*)-> (Sammle Records für erste Seite)
         -(seite)-> (out)
   }


image

3.2. Seite anzeigen {
     (in) -(seite)-> (Normal. Seite auf max Spaltenbreiten)
          -(seite)-> (Formatiere Seite als Tabelle)
          -(string*)-> [Tabelle anzeigen]
   }

image

Jetzt überlegst du, ob du für jede Operation im Flow schon eine konkrete Idee zur Umsetzung hast. Und ob die Umsetzung wahrscheinlich nicht umfangreicher als vielleicht 50 LOC.

Wenn ja, fang an mit dem Codieren, gern nach TDD. Wenn nein, verfeinere weiter, was noch zu kompliziert/unübersichtlich ist.

Wenn du das nicht kannst, dann ist das nicht ein Signal dafür, mit TDD zu beginnen, um Lücken zu schließen, sondern ein Zeichen dafür, dass du das Problem und damit seine mögliche Lösung noch nicht gut genug verstanden hast. Oder vielleicht hast du auch ein Problem mit deinen Technologien. Eine Spike Solution könnte angezeigt sein.

Ein Problem nicht zu verstehen oder keine rechte Idee von der Lösung zu haben, sollte aber allemal ein Warnsignal sein, nicht (!) zu codieren.

Und was ist mit den Daten? Achja... da war doch noch was :-)

Das FD Modell enthält natürlich Daten. Da gibt es Seiten und CSVRecords. Die musst du natürlich auch detaillieren und formalisieren. Aber dazu braucht es nicht mehr als z.B. ein simples Krähenfußdiagramm.

Und was ist mit Funktionalität, die direkt an den Daten hängt? Meine Meinung: die ist überbewertet, weit überbewertet :-)

Dass wir Daten und Funktionen in Klassen zusammenfassen können, ist schön. Das will ich nicht missen. Aber eher nicht für das, was zwischen den Operationen fließt. Das sind Datendaten :-) Datenstrukturen, die im Wesentlichen funktionsfrei bleiben sollten. (ADTs machen da eine Ausnahme.)

Wenn Operationen Zustand haben, dann ist es aber sehr schön, dass ich beides zusammenfassen kann.

Nun hast du zwei ganz einfache Modelle:

1. Ein ganz einfaches Flussmodell für die so wichtige Transformation (V).
2. Und ein ganz einfaches Datenmodell für E und A.


Das nenne ich natürlich, direkt, einfach, verständlich. Kein Rätselraten, sondern ablesen, was in den Anforderungen steht, um es simpelst zu formalisieren.

Verstehst du, was mich motiviert, OO-Technik mit der FD-Methode anzugehen und nicht mit der überkommenen OO-Methode und wie vorteilhaft FD ist?

Mitteilenswert finde ich das unterschiedliche Softwarebild: Maschine vs Verhalten. Denn daraus folgt ein anderer Analyseansatz und eine andere Modellierung.

Und warum ein so anderes Softwarebild? Weil Software sich eben als so volatil erwiesen hat. Maschinen sind statisch, Prozesse hingegen sind (im doppelten Sinn) immer im Fluss. Die Agilität hat das in puncto Vorgehen bei der Softwareentwicklung schon verstanden. Das Softwarebild hinkt mit dem OO-Fokus aber noch hinterher. FP ist ein Lichtblick, doch (noch lange) keine Option für viele Entwickler. Und warum auch dringend Technik (F# statt C#) und Methode (FD statt OOAD) ändern, wenn es (erstmal) reicht, nur die Methode zu ändern? Denn mit FD lässt sich sehr bequem “flüssige Software” entwickeln auf der Basis dessen, was wir gut kennen: OO-Technik.

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat…

Mittwoch, 22. Juni 2011

Lernen mit Format

Das Dojo ist wieder im Gespräch. Ilker hat einen (selbst)kritischen Artikel dazu in seinem Blog geschrieben. Seiner Zurückweisung der Sicht, “dass ein Coding Dojo doch nur ein ‘nettes Rahmenprogramm’ sei” stimme ich zu. Es ist ein “lockeres Mittel zum Training und Erfahrungsaustausch” – auch wenn es mal im Rahmenprogramm einer anderen Veranstaltung auftauchen mag. Gemeinsam lernen: darum geht es beim Coding Dojo.

Aber wie kann es sein, dass das nicht ganz einfach von allen Teilnehmern (oder Ferngebliebenen) auch erkannt wird? Ist das Coding Dojo ein so kryptisches Format?

Meine Vermutung ist, die Missverständnisse hängen weniger am Rahmen, in dem ein Coding Dojo stattfindet – z.B. Konferenz oder Recruiting Day –, als vielmehr an der Art, wie im Dojo mit Lernen umgegangen wird.

Wenn Lernen das Ziel eines Dojos ist, dann sollte eine klare Vorstellung davon existieren, wie Lernen funktioniert. Nur dann ist zu erwarten, dass Lernen auch passiert.

Was gehört also zum Lernen? Was sind die minimalen Zutaten, um ein Dojo zu einem Lernfest zu machen? Ja, ich meine Lernfest, also Lernen im Rahmen eines Festes, nicht nur feste lernen ;-) Lernen soll, nein, muss Spaß machen, sonst ist es nicht effizient und effektiv. Entertainment darf also passieren bei einem Coding Dojo.

Für mich sind die drei unverbrüchlichen Bestandteile von Lernen diese:

  • Klare Aufgabe: Ein Dojo braucht eine klare Aufgabe. Alle Teilnehmer müssen wissen, was sie tun sollen. Dazu gehört die Code Kata als zu lösendes Problem, aber auch das Format (z.B. der Modus, Hilfsmittel, Zeitlimit).
  • Klares Ziel: Die Aufgabe ist ein Mittel, um ein Ziel zu erreichen. Aber welches? Was soll in einem Coding Dojo gelernt werden? Worauf sollen die Teilnehmer während der Aufgabenlösung achten? Ein wiederkehrendes Ziel ist, sich in der Kunst des TDD zu verbessern. Ein anderes Ziel könnte sein, Pair Programming auszuprobieren. Alles Mögliche kann als Lernziel ausgerufen werden: Methoden, Technologien, Konzepte. Wichtig ist lediglich, dass das Lernziel klar und allseits akzeptiert ist.
  • Reflexion: Lernen passiert nicht einfach so. Oder wenn, dann nur unzuverlässig. Lernen braucht vielmehr eine Reflexion. Es braucht Feedback und Nachdenken über den Lernprozess durch die Lernenden. Das nennt sich dann “deliberate practice”. Hier setzt auch das Lehren im Dojo an. Selbst wenn kein ausdrücklicher Lehrer anwesend ist, lehrt die gemeinsame Erfahrung, wenn sie denn reflektiert wird. Die Teilnehmer sind ihre eigenen Lehrer, auch wenn keiner ein Experte in Bezug auf das Lernziel ist. (Experten sind also keine Lernvoraussetzung, sondern eher Lernkatalysatoren; sie beschleunigen das Lernen.)

Wer nun ein Dojo ausrichtet, der ist dafür verantwortlich, dass es ein Lernfest wird. Er muss sich ums Entertainment kümmern – doch das ist nur die Kür. Pflicht hingegen ist, einen Lernerfolg für alle sicherzustellen.

Zur Pflicht gehört es also, erstens eine klare Aufgabe zu formulieren, zweitens ein klares Lernziel anzupeilen und drittens am Ende die Reflexion anzuleiten.

Aufgabe und Lernziel müssen allerdings nicht schon vorher im Kopf des Veranstalters fest sein, können es aber. Ich finde es nicht so wichtig, ob beides vorgegeben ist oder nicht, wenn die Teilnehmer damit kein Problem haben. Enttäuschung über ein Coding Dojo wird eher nicht entstehen, nur weil nicht die persönliche Lieblingskata eines Teilnehmers die Aufgabe ist oder das persönliche Lernziel grad nicht dran kommt. Enttäuschung entsteht, wenn Aufgabe und Ziel nicht erreicht werden – und am Ende unklar ist, warum.

Eine klare Aufgabe, ist selten das Problem eines Coding Dojos. Ein klares Ziel hingegen ist schon seltener. Und noch seltener ist eine gewinnbringende Reflexion. Nach all dem Adrenalin, das während 1-2 Stunden geflossen ist, mag die schwer fallen, doch sie ist unumgänglich. Sie durchzuführen hat nichts Spießiges oder Spaßverderberisches an sich, sondern ist schlicht dem Anspruch eines Dojos geschuldet. Wenn Lernen stattfinden soll, dann ist Reflexion/Feedback nötig.

Hierüber stolpern die meisten Dojos. Niemand mag sich recht zum Moderator einer Reflexion aufschwingen oder die Zeit ist leider schon um. Und so gehen die Teilnehmer immer wieder nach Hause, ohne zu wissen, ob und was sie denn nun gelernt haben.

Eine Besprechung der Aufgabe ist übrigens nicht genug der Reflexion. Es geht vor allem um den kritischen Blick auf die Lernziele. Wie ist man mit TDD umgegangen, wie war das mit Pair Programming oder wie hat man die Technologie X eingesetzt oder die Methode Y?

Und natürlich soll die Reflexion auch auf der Metaebene stattfinden: Wie ist es mit dem Dojo gelaufen? Hat es seinen Zweck erfüllt? Wie war die “Performance” des Veranstalters?

Wo die Reflexion fehlt, da wird ein Dojo zur “Pseudeowissenschaft”, d.h. es wird immun gegen Kritik, da es keinen Rahmen für die Falsifizierung seiner These gibt, die da lautet, dass es ein “lockeres Mittel zum Training und Erfahrungsaustausch” sei, mit dem Lernen zu einem Fest werden kann.

Unterm Strich würde ich sagen: Jedes Coding Dojo ist ein gutes Coding Dojo, wenn es diese drei Bestandteile (oder Phasen) aufweist. Die Kata ist egal, das Entertainment kann minimal oder großartig sein, der Rahmen eine Konferenz oder das Treffen von Kollegen nach Dienst. Aber ohne klare Aufgabe, klares Lernziel und Reflexion am Ende geht es nicht.

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat…

Donnerstag, 16. Juni 2011

Flüssiges SOLID

Es gibt unerwartete Hilfe für das Flow-Design von den objektorientierten Freunden des Clean Code. Innerhalb zweier Tage bin ich über sehr ähnliche, aber unzusammenhängende Aussagen gestolpert.

Da schreibt einerseits der Chefredakteur des ehrwürdigen Dr. Dobb´s Journal, Andrew Binstock, in einem Editorial, wie wichtig er es fände, Klassen klein zu halten:

“Small classes are much easier to understand and to test. If small size is an objective, then the immediate next question is, "How small? Jeff Bay […] suggests the number should be in the 50-60 line range. Essentially, what fits on one screen.”

Und da schreibt andererseits der dänische Fachbuchautor Mark Seemann in seinem Blog:

“Each class is very small, so although you have many of them, understanding what each one does is easy. […] However, when thinking about SOLID code, it actually helps to think about it more like a liquid […]. Each class has much more room to maneuver because it is small and fits together with other classes in many different ways.”

Zwei Autoren stellvertretend für weitere, die dasselbe sagen: Klassen müssen klein sein, um evolvierbaren Code zu erhalten. Das ist ein Schluss, den sie aus anerkannten SOLIDen Prinzipien der Softwareentwicklung ziehen.

Kleine Klassen, nicht nur kleine Methoden

Bevor ich laut darüber nachdenke, was kleine Klassen für den Code bedeuten, möchte ich einen Einwand vorwegnehmen: “Warum sollen Klassen so klein sein und nicht Methoden? Reicht es nicht, wenn jede Methode einer Klassen vielleicht maximal eine Bildschirmseite füllt? Dann ist die doch auch schon viel übersichtlicher als üblich.”

Absolut. Methoden sollten auch nicht umfangreich sein. Jeder Code, der mehr als eine Bildschirmseite umfasst, ist schwieriger zu verstehen. Auf einer Seite können wir Schachtelungen überblicken; wenn wir dafür scrollen müssen, verlieren wir schnell den Zusammenhang.

Aber auch ich finde es nicht genug, wenn nur Methoden klein sind. Denn Methoden sind nicht die für die Laufzeit wesentlichen Funktionseinheiten. In einem objektorientierten Programm geht es eben um Objekte. Wie reden darüber, wie Objekte in Beziehung gesetzt werden. Abhängigkeiten gibt es nicht von Methoden, sondern von Interfaces, d.h. “ganzen” Objekten.

Wenn ich mich durch Code arbeite, will ich daher schnell verstehen, was diese Objekte tun, nicht nur einzelne ihrer Methoden. Ich will die “rekombinierbaren Einheiten” der Software überblicken. Objekte sind mehr als Methoden, da sie Zustand haben können. Das macht ja gerade die Objektorientierung aus: Wir stellen Netzwerke aus zustandsbehafteten Funktionseinheiten her, die Anforderungen erfüllen.

So ein Netzwerk gut zu verstehen, ist die Voraussetzung, es leicht zu verändern. Dazu kommt noch die Granularität seiner Bestandteile, der Objekte. Das ist ja Seemanns Argument: Strukturen aus feingranularen Objekten lassen sich leichter umformen, wenn neue Anforderungen das nötig machen.

Explodierende Abhängigkeiten

Wenn wir uns einig sind, dass Klassen die Funktionseinheiten sein sollten, die klein1 zu halten sind, dann jetzt zu den Folgen.

Die erste offensichtliche Folge ist, dass die Zahl kleiner Klassen sehr viel größer sein muss, als die Zahl der bisherigen. Klassen von mehreren Hundert Zeilen sind keine Seltenheit; solche mit mehreren Zehntausend Zeilen habe ich aber auch schon gesehen. Wenn ich “klein” für den Gedankengang hier einmal mit 100 LOC gleichsetze und übliche Klassen von 300 bis 1000 LOC haben, dann wird die Zahl kleiner Klassen wohl mindestens 5 Mal so groß sein.

Aber nicht nur das. Wenn aus einer Klasse 5 oder mehr werden, dann soll die Summe ja immer noch dasselbe leisten wie vorher. Das heißt, die Funktionalität muss immer noch irgendwie zusammenhängen. Diese vielen Klassen sind also notwendig voneinander abhängig. Wo vorher eine Klasse allein stand…

A

…da ist es zukünftig ein Wald aus Klassen:

A1
  B
  C
    D

A2
  B
  E
  F
    D

(Einrückung bedeutet hier Abhängigkeit, d.h. A1 braucht die Dienste von B und C, C wiederum von D usw.)

Selbstverständlich sind diese Abhängigkeiten nicht alle statisch. Grundsätzlich isolierte Testbarkeit wird erhalten durch dynamische Abhängigkeiten und Dependency Injection. Das ist technisch nicht kompliziert – hat aber seinen Preis beim Testen. Abhängige Klassen können entweder nur mit Integrationstests geprüft werden – was die Fehlerfindung erschwert. Oder sie müssen mit Attrappen ihrer Abhängigkeiten ausgestattet werden – was den Testaufbau selbst mit Mock-Frameworks umständlich macht.

Das hört sich nicht gut an, oder? Steigt die Evolvierbarkeit wirklich, wenn die Klassen klein und übersichtlich werden? Wird da der Teufel nicht mit dem Belzebub ausgetrieben? Vorher war der Code unübersichtlich, weil lang – nun ist der Code unübersichtlich, weil die Abhängigkeiten stark zugenommen haben.

Auch wenn ich das nicht so einfach negativ sehe2, spüre ich auch einen Schmerz bei solcher Zunahme der Abhängigkeiten. Da helfen alle DI Container der Welt nicht. Sie verwalten nur das Elend.

Konzeptionelle Klimmzüge

Doch lassen wir die explodierenden Abhängigkeiten einmal außen vor. Sehen wir sie positiv im Sinne der Prinzipien “Lose Kopplung, hohe Kohäsion” und “Single Responsibility”. Das Entwicklerleben ist halt kein Ponyhof; Opfer sind zu bringen für die Evolvierbarkeit.

Was aber mit den konzeptionellen Klimmzügen die Sie im Rahmen der Klassenverkleinerung vollbringen müssen? Da haben Sie in langen Entwurfssitzungen – oder agilen TDD-Impulsen – nun Ihre Klassen geschnitten; jede ist sorgfältig an die Problemdomäne angepasst; alles hat seine Ordnung – nur leider haben alles Nachdenken und auch der TDD-Druck es nicht geschafft, die Klassen wirklich klein zu halten. 500+ LOC sind herausgekommen für die zentrale Domänenmodellklasse oder die Verschlüsselungsklasse oder die Datenzugriffsklasse. Sie haben sich sogar bemüht, die Methoden klein zu halten. SLA rulez! Und nun kommt einer daher und sagt, wahrhaft SOLIDe clean sei Ihr Code erst, wenn die Klassen selbst klein seien. Ja, wie soll das denn gehen? Ein Kunde ist ein Kunde ist ein Kunde. Den kann man nicht so einfach aufteilen. Und die Verschlüsselungsfunktionalität passt auch so schön unter den Hut einer Klasse.

Diesen Einwand verstehe ich auch sehr gut. Wer die Welt in funktionsreiche Akteure benannt mit Substantiven aufgeteilt hat, der tut sich schwer, diese Funktionalität weiter aufzuteilen. In was sollten Sie einen Kunden denn zerlegen? Naheliegende Abspaltungen von Funktionalität werden Sie schon selbst vorgenommen haben; Sie haben substantivische Sinnzusammenhänge selbstverständlich hergestellt. Die Bonitätsprüfung ist schon nicht mehr Bestandteil des Kunden, sondern eine eigene Klasse, von der der Kunde abhängig ist…

public class Kunde
{
  …
  private IBonitätsprüfung _bp;

  public Kunde(IBonitätsprüfung bp) {…}
  …
}

…damit man hübsch objektorientiert fragen kann: kunde.HatBonitätFür(100000).

Es geht also nicht mehr kleiner, wenn die Problemdomäne sich noch sinnvoll im Code widerspiegeln soll.

Die wunderbaren Flexibilitätsvorteile, die wahrhaft SOLIDer Code durch kleine Klassen verspricht, erscheinen damit unerreichbar. Sie sind dazu verdammt, unSOLIDe zu arbeiten, um nicht in Abhängigkeiten zu ersticken und/oder sich in einem konzeptionellen Wirrwarr zu verlieren.

So scheint es zumindest…

Erlösung durch Perspektivwechsel

So gern Sie den guten Rat von Binstock und Seemann annehmen würden, Sie werden im Augenblick nicht können. Er käme Ihnen teuer zu stehen. Das ist auch meine Meinung. Aus Ihrer Perspektive der üblichen, der OOAD-geprägten Objektorientierung wäre der Preis für den Gewinn an Evolvierbarkeit durch wirklich kleine zu hoch.

Solange Sie aus dieser Perspektive auf den Ratschlag blicken, kann ich Ihnen nicht dazu raten. Was aber, wenn Sie die Perspektive wechseln? Was, wenn Sie sich nicht mehr vor explodierenden Abhängigkeiten fürchten müssten? Was, wenn es keine konzeptionelle Schwierigkeit gäbe, Klassen zu zerteilen?

Eine solche alternative Perspektive gibt es. Es ist die Perspektive des Flow-Designs (FD) und der Event-Based Components (EBC)3.

Im Flow-Design sind Funktionseinheiten (lies: Klassen) nicht mehr voneinander abhängig. Wenn die eine Klasse Daten lädt, die andere sie verarbeitet und eine dritte sie speichert, dann kennen diese Klassen einander nicht, weder statisch noch dynamisch. Es gibt keinen Abhängigkeitsverhau und auch keinen Testattrappenalbtraum.

In FD/EBC gibt es zwar noch Abhängigkeiten, doch die sind streng systematisiert und unkritisch. Kein Grund für Schlaflosigkeit. Die Evolvierbarkeit leidet unter ihnen nicht. Sie folgen einer grundsätzlichen Separation of Concerns.

Darüber hinaus gibt Flow-Design den vorherrschenden Fokus auf Substantive auf. Funktionseinheiten (lies: Klassen) sind keine Akteure, sondern Aktionen. Denken Sie im Augenblick einfach mal nur “zustandsbehaftete Funktion”. Mit FD versuchen Sie zur Lösung eines Problems nicht ein paar Substantive zu finden, auf denen Sie dann in einem umständlichen Verfahren Funktionalität verteilen. Sie suchen vielmehr “nur” nach Funktionen oder “Verhaltensschritten”. Die können Zustand haben oder nicht. Egal. Vor allem Arbeiten Sie auf Input und erzeugen Output.

Dieser Fokuswechsel von Substantiven/Akteuren/Dingen nach Verben/Aktionen/Verhalten führt automatisch zu sehr kleinen Funktionseinheiten (lies: Klassen). Und wenn nicht, dann lassen sich EBC-Klassen (lies: Verhaltensweisen) sehr viel leichter als OOAD-Klassen (lies: Dinge) in weitere Klassen zerlegen.

Fazit

SOLIDer Rat ist tatsächlich viel Wert – wenn Sie ihn im rechten Kontext beherzigen. Solange der die übliche methodische OOAD-Objektorientierung ist, wird es sie schmerzen. Doch mit Flow-Design und Event-Based Components wird aus vormals festen, unhandlich großen Klassen eine SOLIDe Flüssigkeit. Der Entwurf fließt, die Daten fließen, die Strukturen passen sich fließend an.

SOLID ohne Flow-Orientation ist noch fest. Nomen es omen. Doch SOLID mit Flow-Orientation erzeugt Evolvierbarkeit und Testbarkeit. Oder umgekehrt: Flow-Orientation führt automatisch zu SOLIDem Code.

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat…

Fußnoten

1 Ob “klein” nun heißt 25 Zeilen oder 50 oder 75, das finde ich an dieser Stelle nicht so wichtig. Bildschirmseiten bieten ja je nach Bildschirmauflösung und Font und Monitorgröße unterschiedlich viel Platz. Klassen, deren Methoden sehr unabhängig voneinander sind, verstehe ich auch schnell, wenn nur die Methoden leicht zu überblicken sind; dann wäre es ok, wenn ich ein bisschen scrollen muss. Bestehen unter den Methoden aber vielfältige Abhängigkeiten, dann ist das etwas anderes. Dann möchte ich möglichst viel der Funktionalität auf einen Blick sehen. Wie der Berater so schön sagt: Es kommt darauf an. Eine allgemeingültige Zeilenzahl, die niemals überschritten werden darf, lässt sich nicht definieren. Aber ich kann für mich sagen, dass ich unruhig werden, wenn der Umfang einer Klasse mehr als 2-3 Bildschirmseiten hat. Und ich kann nur an Ihre Sensibilität appellieren, den Schmerz zu spüren, wenn Sie zum Verständnis scrollen müssen.

2 Oder genauer: Nicht die Abhängigkeiten haben zugenommen, sondern sie haben sich verändert. Vorher waren sie weniger sichtbar und einfacher. Die Methoden innerhalb einer solchermaßen geshredderten Klassen waren ja auch voneinander abhängig. Diese Abhängigkeiten sind nun explizit gemacht. Das könnte man auch positiv Entkopplung nennen, zu der ebenfalls positiv eine Zusammenfassung nach Kohäsion in neuen Klassen tritt.

3 Über Flow-Design und EBC habe ich ausführlich in diesem Blog und in der dotnetpro geschrieben. Wer mehr dazu erfahren will, finde auf dieser Ressourcenseite Berge an Material: http://clean-code-advisors.com/ressourcen/flow-design-ressourcen