Paradigmenwechsel Wann lohnt der Technologieumstieg – und wie hilft MBSE dabei?

Autor / Redakteur: Andreas Willert / Sebastian Gerstl

Mit dem Flugzeug zum Nachbarn, mit dem Fahrrad nach New York: Das hört sich idiotisch an, aber nur weil wir beide Vorgehen sehr gut kennen und Aufwand und Nutzen genau abschätzen können. Anders sähe es aus, wenn wir uns für den Einsatz einer Fortbewegungsmethode entscheiden sollten, die einem Paradigmenwechsel entspricht, mit dem wir keinerlei Erfahrung haben. Und damit sind wir beim eigentlichen Dilemma angelangt.

Firmen zum Thema

(Bild: gemeinfrei/Pixabay / CC0 )

Was macht einen Paradigmenwechsel aus? Im Wesentlichen, dass es sich nicht um eine lineare Entwicklung handelt, sondern um eine sprunghafte. Das bedeutet auch, dass unsere bisherigen Erfahrungsmuster, nach denen wir Entscheidungen treffen, nicht mehr gelten. Das wiederum erhöht die Wahrscheinlichkeit von ungünstigen Entscheidungen deutlich. Jede ungünstig getroffene Entscheidung führt in Folge zu Erfahrung. Mit dem Grad der Erfahrung verbessert sich dann wieder die Qualität der Entscheidungen.

Was genau ist ein Paradigmenwechsel?

Bei einem Paradigmenwechsel ändern sich die gelernten Gesetzmäßigkeiten sprunghaft. Unsere bisherigen Entscheidungsgrundlagen greifen nicht mehr und das führt zu Fehlentscheidungen. Aber warum sind Paradigmenwechsel notwendig und vor allem wann?

Bildergalerie

Gehen wir im wahrsten Sinne des Wortes noch einmal einen Schritt zurück im Paradigma, zur Fortbewegung mit den Füßen. In Afrika gibt es noch sehr viele Menschen, bei denen diese Paradigma Hauptfortbewegungsmittel ist. Nehmen wir z.B. Äthiopien oder Kenia. Aus diesen beiden Ländern kommen die besten Läufer der Welt. Sie haben das Laufen zur Perfektion entwickelt. Sie können sowohl extrem lange Distanzen überbrücken, als auch hohe Geschwindigkeiten erreichen. Wie sind sie dazu gekommen? Es gibt dort schlicht keine anderen Fortbewegungsmittel bzw. sind deren notwendige Infrastruktur nicht finanzierbar.

Nicht selten läuft dort ein Kind jeden Tag 5 km zur Schule und wieder zurück. Wer das macht hat Laufstil, Kondition etc. hochgradig optimiert. Stellen wir uns nun vor die Schule im Nachbardorf wird geschlossen und die nächste Schule ist 10 km entfernt. Dann würde dieses Kind wahrscheinlich seine Kondition an die neue Entfernung anpassen, früher aufstehen und später nach Hause kommen und evtl. in der Schule nicht mehr ganz so aufmerksam sein. Das Kind ist begabt und soll nun eine gehobene Schule besuchen. Die ist 15 km entfernt. Das Kind steht noch früher auf und kommt noch später nach Hause, bekommt Muskelkater, der wieder vergeht und schläft evtl. in der Schule hin und wieder ein, was nicht vergeht. Der Grund für letzteres ist nicht das Laufen an sich, sondern der latente Schlafmangel.

Denken wir obiges Modell konsequent zu Ende könnte uns folgende Erkenntnis ereilen: Es kommt irgendwann der Punkt an dem die physikalische Grenze erreicht ist, spätestens, wenn die reine Laufzeit länger als 24 Std. dauern würde. Aber zu diesem Punkt wird es wahrscheinlich niemals kommen, bereits vorher kollabiert das System in Folge von divergenten Symptomen, die offensichtlich erst einmal wenig mit dem Laufen in Zusammenhang stehen. Es ist also ein schleichender Prozess der Verschlimmerung. Es gibt also nur sich häufende, latente, unspezifische Symptome, die das Gesamtsystem ineffizient machen. Sie stehen jedoch selten mit dem Vorgehen in direktem Zusammenhang. Das wäre evtl. eine Sehnenscheidenentzündung durch Überlastung.

Vergleichen wir obiges Paradigma zur Bewegung mit Fahrradfahren, dann wird ersichtlich: Wir können das Paradigma „zu Fuß gehen“ noch so optimieren, wir erreichen niemals die Möglichkeiten des Fahrradfahrens. Im Triathlon lassen sich die Paradigmen sehr gut vergleichen. Hier stehen 42 km laufen mit einer Zeit von ca. 2:30 h dem Radfahren 180 km mit Zeiten von 4:04 h gegenüber. Sicher können wir davon ausgehen, das beide Paradigmen gleichwertig optimiert angewandt werden. Weder die Reichweite, noch die Geschwindigkeit des Radfahrens können mit dem Paradigma Laufen auch nur annähernd erreicht werden. Aber es gibt noch einen weiteren Unterschied. Die Basisinvestitionen für den erfolgreichen Einsatz eines Paradigmas liegen erheblich über dem vorherigen. Laufschuhe (150,- €) stehen einem Fahrrad (1.500,- €) gegenüber.

Die Investitionen sind der häufigste Hinderungsgrund, warum der Schritt zu einem neuen Paradigma nicht zum optimalen Zeitpunkt gemacht wird. Es besteht die Neigung, das Risiko zu minimieren und den großen Schritt in kleinere Schritte zu teilen. Dasselbe gilt für das Beherrschen der Fertigkeiten. Selbst das teuerste Fahrrad kann die Technik des Fahrradfahrens nicht ersetzen.

Noch größer wird der Unterschied, wenn wir das Paradigma Autofahren nehmen. Es muss ein Führerschein gemacht werden, Versicherungen und Steuer bezahlt, ein PKW angeschafft werden.

Das gleiche gilt im Software Engineering. Die Basiskosten für die Einführung einer MDSE-Umgebung liegen um ein Vielfaches über den Kosten einer Umgebung zur prozeduralen Programmierung mit einer Hochsprache. Dasselbe gilt für die Einarbeitungszeiten.

Paradigmenwechsel bedingen deutlich höhere Basisaufwendungen zur Einführung und zum Erhalt. Aber niemand käme auf die Idee, aus diesem Grund bei Distanzen über 200 km die Sinnhaftigkeit eines PKWs gegenüber dem Laufen in Frage zu stellen. Auf der anderen Seite kämen wir auch nicht auf die Idee, das Auto zu nutzen, wenn wir zum Nachbarn möchten. Das neue Paradigma ersetzt das alte niemals vollständig. Es ergänzt es in den Situationen, in denen das vorherige an seine Grenzen stößt.

Stellen wir uns noch einmal den Wechsel zum Beispiel vom Fahrrad auf das Paradigma Auto vor. Wenn Sie den Beweis möchten, ob das Paradigma größere Distanzen in kürzerer Zeit zu überbrücken wirklich funktioniert, und erst einmal eine Karosserie, um Ihren Pedalantrieb anschaffen, bevor Sie in einen teuren Motor investieren, werden Sie den zweiten Schritt wahrscheinlich niemals tun und den Versuch als gescheitert abbrechen. Denn wahrscheinlich werden Sie mit obiger Apparatur langsamer sein, als ohne. Auch umgekehrt, in Ihr Fahrrad einen Ottomotor zu bauen, wird nur eingeschränkte Erfolge haben. Wenn nicht gleichzeitig Rahmen, Bremsen, etc. angepasst werden, kann das sogar tödlich enden.

Paradigmenwechsel verlaufen nicht linear

Paradigmenwechsel sind Sprünge, sie verlaufen nicht linear, und lassen sich nicht in kleine Schritte aufteilen. Das ist es aber, was sehr häufig versucht wird, um das Risiko einer Fehlinvestition zu minimieren. Erst einmal lediglich die UML zu nehmen, um die Software bildhaft darzustellen. Sein wir doch einmal ehrlich, bildhaft darstellen, das machen wir doch schon lange. Immer wenn wir über Software sprechen, zeichnen wir Grafiken an Flipcharts, Zeichen-Diagramme in Visio. Was sollte sich ändern, wenn wir dasselbe nun in UML tun? Das Hauptproblem bei diesem Vorgehen ist doch, dass Dokumentation und Code auseinander laufen und daran ändert sich nichts.

Oder die Anschaffung eines grafischen UML-Editors, ohne die Möglichkeit der Modell-Simulation: Das ist wie ein Auto ohne Motor. Ja, sie werden nun nicht mehr nass, wenn es regnet (auch ein Vorteil), aber das eigentliche Ziel, schneller weitere Distanzen zu überwinden, werden Sie nicht validieren können.

Paradigmenwechsel in kleine Schritte aufzuteilen, funktioniert nur sehr begrenzt. In der Praxis ist es in der Regel hinausgeschmissenes Geld, weil die damit verbundenen Aufwendungen nicht amortisiert werden. Eventuell ist es sogar gefährlich, weil eine Fehlbeurteilung des möglichen Nutzens stattfindet und der Paradigmenwechsel abgebrochen wird und die notwendige Entwicklung nicht stattfindet. Was sind nun die konkreten Vorteile von MDSE, z.B. in der Notation UML im Vergleich von prozessualer Programmierung in einer Hochsprache wie ANSI C.

Werfen wir dazu noch einmal einen kurzen Blick auf die zu bewältigenden Herausforderungen. Es gilt die steigende Komplexität zu bewältigen. Dabei sind Hidden Links, Emergenz und Disfunktion die Hauptmerkmale, mit denen sich Komplexität in seiner negativen Form zeigt. Sie führen uns durch folgende Betrachtungen (siehe Bild 1).

Include-Beziehungen und Component-Based Design

Bild 2: Unsaubere Beziehungsstruktur
Bild 2: Unsaubere Beziehungsstruktur
(Bild: Willert Software Tools)

Teile und herrsche ist der meist eingesetzte Engineering-Schritt, Komplexität zu begegnen. In C wird dem mit Modulen und Funktionen begegnet. Jedes Mal, wenn ein System in kleinere Komponenten geteilt wird, entstehen Schnittstellen. Ein Teil der Komplexität wird dabei auf die Beziehungsebenen verschoben. In ANSI C wird die Struktur der Beziehungen durch Include-Beziehungen repräsentiert. Diese Struktur ist jedoch nicht explizit zu sehen und lästig zu managen, was dazu führt, dass in den meisten Projekten über eine globale Include-Struktur alles mit allem in Beziehung steht. Das führt dazu, dass auch alles mit allem kommunizieren kann, was zu sogenannten Hidden Links führt. Diese Hidden Links erhöhen bei Änderungen das Risiko für Emergenz (Systeme kommen in nicht gewollte Zustände), und damit zum Fehlverhalten.

In UML können Beziehungsstrukturen ganz gezielt grafisch modelliert werden. Daraus werden eindeutige Include-Beziehungen in C generiert. Wenn ein Programmierer nicht dieser Interface-Struktur folgt wird spätestens der Linker die Arbeit versagen. Das ist eine sehr effiziente Hilfe Hidden Links vorzubeugen. Durch grafische Visualisierung und daraus erzeugten Include-Beziehungen lassen sich sehr effektiv Hidden Links vermeiden, was bei steigender Komplexität weniger häufig zu Emergenz und Disfunktion führt.

Spezifikation und Test-Automatisierung

Die Notation UML besitzt im Gegensatz zu herkömmlichen Hochsprachen wie ANSI C oder C++ Notationselemente, die die Spezifikation von Systemen adressieren (UseCase-, Sequenz- und Timing-Diagramme, Bild 3 und 4). Die Mächtigkeit dieser Diagramme wird häufig unterschätzt. Das liegt auch daran, dass dem Anforderungsmanagement im Allgemeinen nicht besonders viel Aufmerksamkeit geschenkt wird und direkt implementiert wird. Aber spätestens beim Testen werden die eigentlichen Anforderungen benötigt. Gegen was soll denn die Implementation getestet werden?

Also in jedem Projekt wird im eigentlichen Sinn Anforderungsmanagement betrieben. Entweder zum günstigen Zeitpunkt am Anfang des Projektes oder zum weniger günstigen Zeitpunkt am Ende des Projektes, wenn die Tests spezifiziert werden (vorausgesetzt, dass halbwegs ordentlich getestet wird).

Wenn nun die Spezifikation die Quelle der Tests sind, was läge näher, als die Tests zu spezifizieren. Genau das ist z.B. auf Basis von Sequenz-Diagrammen möglich. Zum Beginn eines Projektes dienen sie dazu, sich klar zu machen, wie das System in bestimmten Situationen zu reagieren hat, welche Funktionen es bekommen muss …. Diese Diagramme können in Folge dann sehr einfach dazu dienen, die ersten Implementierungen zu testen. Das Ganze kann sogar automatisiert in der Nacht geschehen (Nightly Tests).

Das Vorgehen kann auch als TDD (Test Driven Development) gesehen werden, was nachweislich Qualität von Software erhöht. Dieses Vorgehen ist auf Basis von UML und geeigneten Werkzeugen sehr viel einfacher möglich, als auf Basis von Hochsprachen. Auf Basis von standardisierten Notationselementen zur Spezifikation von Systemen lassen sich mit geringem Aufwand automatisierte Tests erstellen. Dieses ist die ideale Voraussetzung für Nightly Tests und Test Driven Design als eine der mächtigsten Instrumente zur Qualitätsabsicherung.

Abstraktion und Muster - Contract Based Design

Zustandsmaschinen repräsentieren die Kombination von Mustern und darauf basierender Abstraktion. Die Visualisierung einer simplen Zustandsmaschine mit zwei Zuständen und zwei Zustandsübergängen auf Basis eines Zustand-Diagramms enthält sechs Symbole (zwei für den formal notwendigen Default State). Ein korrespondierender Code mit allen notwendigen Implementierungen würde im simpelsten Fall ca. 200 Zeilen lang sein.

Zum Beispiel stellt das Event Handling mit Event Queue ein Muster dar, das genauen Regeln folgt, welche in der Zustandsmaschine implizit enthalten bzw. berücksichtigt sind. Das Verhalten der Zustandsmaschine lässt sich sehr exakt verstehen, ohne das Kenntnisse über die genauen Implementationen vorhanden sein müssen. Sehr viel wichtiger sind genaue Kenntnisse über die Regeln (Contract), dem dieses Muster folgt.

Es gibt beispielsweise die Regel, dass Events, auf die zum aktuellen Zeitpunkt kein Zustand wartet, weggeschmissen werden. Ansonsten würden sie die Queue blockieren. Das macht durchaus Sinn. Wenn die Regel jedoch nicht bekannt ist und beim Design der Zustandsmaschine nicht berücksichtigt wird, kann es zu Fehlern führen. Umgekehrt ist es genau so: Werhält sich die Implementation des Musters nicht so, wie definiert, führt auch das zu Fehlverhalten. Leider wird die Notwendigkeit, die Spezifikation der Muster sehr gut zu kennen, häufig nicht ernst genug genommen und Muster werden nicht derart verwandt, wie sie ursprünglich gedacht waren.

Wie mächtig Muster sind, kann sehr deutlich erkannt werden, würde der generierte Code aus folgenden beiden Zustandsmaschinen betrachtet. Hier ist nicht nur Code hinzugekommen, sondern es bleibt im wahrsten Sinn des Wortes keine Zeile Code mehr auf der anderen. Würde man dieselbe Änderung auf Code-Ebene durchführen, würden wahrscheinlich aus Bequemlichkeit den Zuständen Rucksäcke verpasst werden. Das würde sich jedoch nachteilig, sowohl auf Codegröße, Laufzeit und Verstehbarkeit auswirken und die Komplexität unnötig erhöhen. Einem Codegenerator ist der Aufwand der Änderung egal. Er erzeugt in Sekunden-Bruchteilen wieder neuen sauberen Code.

Auf Basis von Abstraktion und Mustern lässt sich Software sehr viel einfacher und vergleichsweise sicherer verstehen, ändern und erweitern, als auf Basis von C-Code. Darüber hinaus lässt sich die inhärente Qualität bei Änderungen und Erweiterungen hoch halten.

Frühe Simulation - Frontloading

Bleiben wir gleich bei der vorherigen Zustandsmaschine. Sehr häufig wird Genauigkeit (Exaktheit) mit Detailliertheit verwechselt. Immer wieder höre ich, dass eine frühe Simulation auf Basis von UML nicht möglich ist, da ja erst simuliert werden kann, wenn das Modell mit ausreichend Details angereichert wurde. Das ist schlicht falsch. Das Verhalten obiger Zustandsmaschine kann genau in diesem Grad der Detaillierung bereits simuliert werden. Lediglich die Namen der Events müssen derart definiert werden, dass sie kompilierbar sind (Den gültigen Symbol-Definitionen der zukünftigen Action Language folgen).

Dann kann die Logik der Zustandsmaschine simuliert werden. Aber es geht noch weiter, erinnern Sie sich an Spezifikation auf Basis von Sequenz-Diagrammen. Schon zu diesem Zeitpunkt kann getestet werden, ob die Logik der Zustandsmaschine der Definition der Sequenzen entspricht und diese einhält.

Alles zusammen ist mehr als die Summe der Einzelteile

Es ließen sich noch unzählige weitere Vorteile aufzählen, aber es ist nicht notwendig, um aufzuzeigen, dass in jedem einzelnen Punkt mehrere Elemente des Paradigmenwechsels zusammen wirken. Erst in diesem Zusammenwirken entfaltet sich das Potential. Im Wesentlichen sind es folgende Elemente, die immer wieder auftreten, um nur einige der wichtigsten Wirkmechanismen zu nennen:

  • Die korrekte Anwendung der Muster in genau dem Sinn, wie sie definiert sind (Contract based). Ist das nicht gegeben ist die Anwendung von Mustern eher kontraproduktiv.
  • Abstraktion auf Basis von grafischen Repräsentationen, um die Verstehbarkeit zu erhöhen.
  • Exakte Transformation der Modelle in verschiedene Abstraktionsräume. Datenfluss in Zeitverhalten, Klassen-Struktur in Include-Beziehungen etc.
  • Code Generierung für Simulation und Produktion
  • Backannotation von Laufzeitinformationen, um Debugging auf dem Grad der Abstraktion zu ermöglichen, auf der die Implementation stattfindet und Tests zu automatisieren.

Fazit:

Der ganz normale Wahnsinn

Stellen Sie sich vor, sie haben eine Zustandsmaschine in einem grafischen UML Editor gezeichnet. Aus statischer Sicht der grafischen Repräsentanz sind Sie sich sicher, dass diese Zustandsmaschine das tut, was sie soll. Nun codieren Sie die Zustandsmaschine, compilieren, linken, laden Sie in den Debugger und lassen sie laufen. Auf den ersten Blick scheint alles richtig zu sein. Aber eventuell sollte doch noch etwas vollständiger getestet werden. Sie arbeiten strukturiert und erstellen sofort die entsprechende Testspezifikation. Aber was musste die Zustandsmaschine noch einmal alles tun können, auf welche Stimuli wie reagieren?

Keine Sorge, sie finden es nachträglich wieder heraus und nach einiger Zeit laufen die ersten Tests und ergeben, dass doch tatsächlich die Reaktion auf einen Stimuli in zwei Zuständen nicht berücksichtigt wurde. Nun gehen Sie zurück in den grafischen Editor, um die Zustandsmaschine entsprechend anzupassen? Nein ich befürchte das tun sie nicht, da sie ja gerade in Ihrer geliebten C-Umgebung sind, ist der Code ja schnell geändert, und wenn die Änderung dann alle Tests passed, dann wird das Diagramm entsprechen nachgezogen. Aber als Sie dann endlich alles am Laufen haben, zeigt die Uhr 19:30 und um 20:00 beginnt das Theater‚ im wahrsten Sinn des Wortes mit Ihrer Frau, wenn sie nicht sofort losfahren.

Am nächsten Tag warten im Büro wieder so viele Dinge auf Sie, dass Sie an alles andere denken, aber nicht daran, die Zustandsmaschine nachzuziehen.

Es könnte auch so aussehen

Sie überlegen, auf welche Stimuli Ihre Zustandsmaschine wie reagieren muss, und spezifizieren Ihre Überlegungen sofort in Form von Sequenz-Diagrammen. Wenn Sie der Meinung sind, dass alle wichtigen Stimuli entsprechend berücksichtigt sind, fangen Sie an, die Zustandsmaschine grafisch zu modellieren.

Wenn Sie der Meinung sind, dass die Zustandsmaschine alle Sequenzen erfüllt, drücken Sie auf einen Knopf, generieren den entsprechenden Code, laden Sie in den UML Target Debugger und spielen ein paar Szenarien durch, die Implementation scheint gelungen. Die Uhr zeigt 18:00, genau früh genug, um Ihre Frau zu überraschen und noch vor dem Theater zum Essen einzuladen.

Am nächsten Morgen haben Sie die Ergebnisse der Nightly Tests in Ihrem Postfach. Sie wurden auf Basis der Sequenzdiagramme automatisch erstellt. Die Zustandsmaschine hat in zwei Zuständen auf ein Event nicht reagiert. Sie korrigieren die Zustandsmaschine schnell auf Diagramm-Ebene, generieren Code. Das war es! (Vorausgesetzt Sie haben mit den Änderungen nicht noch einmal einen neuen Fehler eingebaut, aber das würden Sie spätestens morgen früh nach dem nächsten Nightly Test merken).

Spezifikation, Testdefinition, Code … alles ist in sich konsistent - ja und Sie werden es nicht glauben - auch die Dokumentation.

(Dieser Beitrag wurde mit freundlicher Genehmigung der Autoren dem Tagungsband Embedded Software Engineering Kongress 2015 entnommen)

* Walter van der Heiden hat praktische Erfahrung im Bereich Embedded Softwareentwicklung und technisches Coaching in Embedded SW Projekten. Derzeit ist er CTO von Willert Software Tools.

* Andreas Willert beschäftigt sich seit 10 Jahren mit MDD, Requirement Engineering und Qualitätssicherung im Umfeld von Embedded Systemen. Neben der Tätigkeit als Geschäftsführer der Firma Willert Software Tools GmbH gibt er seine Erfahrungen in diesem Umfeld als Autor, Trainer und Referent weiter.

Artikelfiles und Artikellinks

(ID:44117894)

Über den Autor