Direkt zum Hauptbereich

Vom Entwurf zum Programm




Was lernt man nicht alles für feine Dinge als Softwaretechniker: Die Software-Entwürfe entstehen am Reißbrett, oftmals hart erarbeitet, und sie präsentieren sich als schicke Diagramme. Dabei bedienen wir uns einer "Geheimnotation" namens Unified Modeling Language (UML), die für Laien kaum verständlich ist. Da gibt es Kästchen, Linien, Rauten, Zahlen, Benamungen, Textanmerkungen -- das wirkt alles sehr imposant. Es braucht einiges an Zeit, ehe man die hohe Kunst des Software-Entwurfs erlernt hat.

Irgendwann findet man sich jedoch auf der Programmier-Baustelle wieder. Hier werden die Entwürfe in für einen Computer verständliche Anweisungen umgesetzt. Und, wenn man genau hinschaut, geht es hier schlimmer zu als auf jeder echten Baustelle. Der Programmcode hat nur lose Ähnlichkeiten mit den Entwürfen. Programmierer ziehen mal hier und mal dort eine Wand ein, halten an anderer Stelle einen Durchbruch für angemessen, und das Maßband scheint ihnen völlig abhanden gekommen zu sein. "Pi mal Daumen" -- ja, so ungefähr entspricht das dem Entwurf. Wer seine Aufgabe als Bauherr auf der Programmier-Baustelle ernst nimmt, der kann verzweifeln.

Ein kleines Beispiel gefällig?

Schauen Sie sich mal oben das Bild an. Ein UML-Diagramm, das ich in einer Vorlesung nutze. Es ist ein Entwurf für ein OO-Framework. Hübsch anzusehen, gell? Ich weiß, das Diagramm ist nicht perfekt -- aber darauf kommt es mir hier nicht an.

Zu diesem OO-Framework gibt es eine einfache Implementierung in Python von mir. Auch die spreche ich mit den Studierenden durch. Vor wenigen Wochen, als ich versuchte, den Code zu erklären, griff ich zur Kreide. Ich begann, die Referenzbezüge, so wie sie im Python-Code aufgebaut sind, in einer informellen Notation an die Tafel zu malen, in der Hoffnung, so werde sich der Wald lichten. Tat er aber nicht. Das Ganze entwickelte sich zu einer wunderschönen Fallstudie, wie schwer sich Entwurf und Implementierung tun, aufeinander zu passen.

Was haben die beiden Bilder gemeinsam? Man erkennt Ähnlichkeiten. Die großen Kästchen tragen die gleichen Namen. Aber sonst? An der Tafel sind verwirrend viele Linien hinzugekommen. Wie kommt's?

Ein Kernproblem ist, dass die Ausdrucksmittel aus dem UML-Diagramm keine klare Entsprechung in meiner Zielsprache, der Programmiersprache Python haben. Ich muss also mit "Workarounds" arbeiten, die die Intentionen aus dem Entwurf umsetzen. Zum Beispiel die Kompositionen. In der UML baut eine Komposition besondere Zugriffsbezüge und Abhängigkeiten in den Lebensbezügen auf. In den gängigen OO-Sprachen, wie auch in Python, Java, C# oder Ruby gibt es jedoch keine Komposition. Die meisten OO-Sprachen basieren auf Objekt-Referenzen, die keine Zugriffsbezüge durch Namensräume regeln, jedoch auf den Lebenszyklus von Objekten einen erheblichen Einfluss haben. Also bildet man die Intention der Komposition auf anderen Wegen ab und sorgt sich um einen gesitteten Umgang mit Referenzen. Ähnliches gilt für das "instanceOf".

Und hier gehen die Probleme los. Wer das intendierte Mapping von Code und Entwurf nicht kennt -- dieses Mapping wird nämlich selten, eher gar nicht dokumentiert --, der steht in der Gefahr, irgendwann einmal am Code Änderungen vorzunehmen, die den Entwurf verletzen und der austarierten Balance von Code und Entwurf radikal Schaden zufügt. So entstehen Fehler, die in aller Regel schwer zu entdecken, wenn, dann meist folgenreich sind und deren Beseitigung erhebliche Anstregungen kostet.

Ein anderes Beispiel. In Python nutze ich gerne Dictionaries (sie kennen ähnliches vielleicht als HashMaps oder assoziatives Arrays) -- und zwar als Caches für einen schnellen Zugriff auf Objekte unter einem Schlüssel. Es sind die "Qualifier" (die kleinen Boxen an den großen Boxen) auf dem Tafelbild. Streng genommen, mit Blick auf den Entwurf, bräuchte ich sowas nicht. Doch der Einsatz von Dictionaries macht den Python-Code oft einfacher und übersichtlicher. Der Preis: Ich entferne mich vom Entwurf. Die Dictionaries beginnen ein Eigenleben zu führen.

Wenn Sie Studentin oder Student der Informatik sind, dann haben sie wahrscheinlich zahllose, weitere solche Beispiele auf Lager, die einen erheblichen "Clash" aufzeigen zwischen Entwurf und Umsetzung. Bisweilen entsteht das UML-Diagramm zum Entwurf erst nachträglich, nach der Programmierung, und schönt und verhübscht die "Häßlichkeiten" des Codes erheblich.

Sind Codegeneratoren ein Ausweg, die aus UML-Diagrammen direkt den Programm-Code (z.B. für Python) erzeugen? Jaein. Zum einen ist es gar nicht so einfach, gute schematische Code-Templates für die Code-Generation zu entwickeln. Der Versuch, Komposition sauber in einer rein referenz-basierten Sprache nachzubilden, ist nicht wenig aufwendig und versucht einer Sprache etwas überzustülpen, wofür sie nicht gemacht wurde. Das ist zumindest bei einem Blick auf das Laufzeitverhalten nicht ganz außer acht zu lassen. Allein die Nachbildung von Singletons, ein lapidarer Stereotyp in der UML, ist nicht trivial, besonders wenn Singletons thread-safe sein sollen.

Doch das eigentliche Problem liegt in meinen Augen tiefer. Code-Generierung aus Entwürfen ist ok, sofern man einen ausführbaren Entwurf haben möchte. Nicht immer ist das sinnvoll. Wichtig ist zu erkennen, dass es einen Unterschied gibt zwischen Entwurf (Design) und Umsetzung (Implementierung). Eine Umsetzung muss einen Entwurf erfüllen. Nicht mehr und nicht weniger. Das wird wenig verstanden. Ich habe versucht in meinen Posts zu User, Domain und Realization Model darauf in einem anderen Kontext einzugehen. Denn häufig sehe ich nicht nur Studierende, sondern auch Profis einen Fehler machen: Es wird weniger über einen Entwurf nachgedacht! Es wird Programmcode mit Hilfe von UML-Diagrammen beschrieben, nicht ein Entwurf! Dann wird der Code-Generator nur zum Erfüllungsgehilfen von mit Diagrammen "gemaltem" Code.

Glauben Sie nicht? Wann haben Sie das letzte Mal Mehrfachvererbung angewendet? Das wird Ihnen als Ausdrucksmittel in der UML für Ihre Entwürfe angeboten und kann ganz hilfreich sein. Was? Mehrfachvererbung nutzen Sie nicht? Warum? Weil Sie Java- oder C#-Programmierer sind? Erwischt! :-)

Beliebte Posts aus diesem Blog

Lidl und der Kassen-Bug

Es gibt Fehler, im Informatiker-Jargon "Bugs", die etwas anrühriges haben. Ich bat den Menschen an der Kasse bei Lidl um einen Moment Geduld und meine Kinder um Ruhe, um nicht den wunderbaren Moment zu verpassen, bei dem es passierte. Der Lidl-Mensch fluchte kurz auf -- und ich war entzückt! "Einen Moment, davon muss ich ein Foto machen!" Und dann machte ich noch eines. Ich bin heute extra für diesen Fehler zu Lidl gepilgert -- ich wollte es mit eigenen Augen sehen. Gestern hat mir ein Student (vielen Dank Herr Breyer) von diesem Fehler in einer EMail berichtet. Ein richtig schöner Fehler, ein Klassiker geradezu. Ein Fehler, den man selten zu Gesicht bekommt, so einer mit Museumswert. Dafür wäre ich sogar noch weiter gereist als bis zum nächsten Lidl. Der Fehler tritt auf, wenn Sie an der Kasse Waren im Wert von 0 Euro (Null Euro) bezahlen. Dann streikt das System. Die kurze Einkaufsliste dazu: Geben Sie zwei Pfandflaschen zurück und Lidl steht mit 50 Cent bei Ihne...

Syntax und Semantik

Was ist Syntax, was ist Semantik? Diese zwei Begriffe beschäftigen mich immer wieder, siehe zum Beispiel auch " Uniform Syntax " (23. Feb. 2007). Beide Begriffe spielen eine entscheidende Rolle bei jeder Art von maschinell-verarbeitbarer Sprache. Vom Dritten im Bunde, der Pragmatik, will ich an dieser Stelle ganz absehen. Die Syntax bezieht sich auf die Form und die Struktur von Zeichen in einer Sprache, ohne auf die Bedeutung der verwendeten Zeichen in den Formen und Strukturen einzugehen. Syntaktisch korrekte Ausdrücke werden auch als "wohlgeformt" ( well-formed ) bezeichnet. Die Semantik befasst sich mit der Bedeutung syntaktisch korrekter Zeichenfolgen einer Sprache. Im Zusammenhang mit Programmiersprachen bedeutet Semantik die Beschreibung des Verhaltens, das mit einer Interpretation (Auslegung) eines syntaktisch korrekten Ausdrucks verbunden ist. [Die obigen Begriffserläuterungen sind angelehnt an das Buch von Kenneth Slonneger und Barry L. Kurtz: Formal Syn...

Factor @ Heilbronn University

It was an experiment -- and it went much better than I had imagined: I used Factor (a concatenative programming language) as the subject of study in a project week at Heilbronn University in a course called "Software Engineering of Complex Systems" (SECS). Maybe we are the first university in the world, where concatenative languages in general and Factor in specific are used and studied. Factor is the most mature concatenative programming language around. Its creator, Slava Pestov, and some few developers have done an excellent job. Why concatenative programming? Why Factor? Over the years I experimented with a lot of different languages and approaches. I ran experiments using Python, Scheme and also Prolog in my course. It turned out that I found myself mainly teaching how to program in Python, Scheme or Prolog (which still is something valuable for the students) instead of covering my main issue of concern: mastering complexity. In another approach I used XML as a tool ...