Wann ist modellgetriebene Softwareentwicklung sinnvoll?

In diesem Artikel soll nach einer Einführung in das Thema der in Entwicklerkreisen etwas verrufenen modellgetriebenen Softwareentwicklung das Entwicklungsverfahren allgemein evaluiert werden.

Worum geht es bei der modellgetriebenen Softwareentwicklung?

Bei der modellgetriebenen Softwareentwicklung geht es darum, aus abstrakten Modellen Quellcode zu erzeugen und aus dem Quellcode möglicherweise wieder ein formal korrektes Modell zu erzeugen. Im Optimalfall sollte ein Fachexperte ohne Programmierkenntnisse in der Lage sein unter Zuhilfenahme modellgetriebener Softwareentwicklung seinen Sachverhalt formal zu modellieren und sich ein Programm zur Lösung einer beliebigen Aufgabe zu generieren. Dies könnte theoretisch die Entwicklungsgeschwindigkeit erhöhen und den Beruf des Softwareentwicklers überflüssig machen. Um das genauer zu verstehen, muss zunächst der Begriff des Modells definiert werden.

Wie sieht ein Modell in der modellgetriebenen Softwareentwicklung aus?

Ein Modell ist in der modellgetriebenen Softwareentwicklung eine abstrakte Repräsentation eines Systems mit reduzierter Komplexität [7]. Es soll dabei helfen, das „Big Picture“ besser zu erkennen und eine Übersicht über ein System zu erhalten. Modelle bestehen aus einer Menge an formalen Aussagen und Restriktionen. Das System, welches von diesen Aussagen und Restriktionen beschrieben wird, nennt man auch „System under Study“ (SUS) [15].

In der Praxis werden Modelle meist mit der Unified Modeling Language (UML) [10] erstellt. Laut OMG UML, welche häufig als Leitfaden für Modelle und modellgetriebene Entwicklung zitiert wird, gibt es zwei grundlegende Typen von Modellen.

  1. Das beschreibende Modell. Es geht davon aus, dass das SUS bereits existiert. Mit dem beschreibenden Modell wird versucht, dieses System bestmöglich abzubilden. Das Modell gilt als formal korrekt, wenn es das SUS korrekt abbildet.
  2. Das Spezifikationsmodell. Hier existiert noch kein System under Study. Stattdessen wird das System durch das Modell erst spezifiziert. Somit ist das Modell per Definition korrekt. Statt des Modells kann das System auf Validität geprüft werden.

Auf Basis dieser zwei Modelle gibt es in der modellgetriebenen Softwareentwicklung zwei Ansätze.

Translationist vs. Elaborationist

Beim ersten Ansatz werden ausschließlich Spezifikationsmodelle verwendet. Dabei wird ein vollständiges Modell erstellt, aus welchem dann der komplette Quellcode generiert wird. Änderungen im Quellcode, welche dann wieder zurück ins Modell übersetzt werden können, sind nicht vorgesehen. Diesen Ansatz nennt man auch „Translationist“ [9].

Auf der anderen Seite gibt es den Ansatz des „Elaborationist“ nach A. Kleppe [8]. Dort kann nicht nur ein Spezifikationsmodell definiert werden, aus dem dann Quellcode erzeugt wird. Stattdessen kann der existierende Quellcode anschließend auch angepasst werden. Daraufhin soll es möglich sein, das formale Modell auf Basis der Quellcodeänderungen zu aktualisieren. Dieser Ansatz wird auch „Round-Trip-Engineering“ genannt [13]. Es ist jedoch zu beachten, dass die Komplexität für die Berechnung der Rückrichtung (Quellcode -> Modell) sehr hoch ist und die resultierenden Modelle häufig nicht mehr den gewünschten Überblick in menschenlesbarer Form bieten.

Eine umfangreichere Gegenüberstellung der beiden Ansätze ist zu Beginn einer Arbeit von D. Ameller [2] zu finden.

Wie funktioniert modellgetriebene Softwareentwicklung im Groben?

Wie oben bereits angedeutet, geht es bei der modellgetriebenen Softwareentwicklung im Allgemein darum, aus einem abstrakten Modell konkreten Quellcode, sowie gegebenenfalls die Rückrichtung zu erzeugen. Dabei werden in mehreren Zwischenschritten Modelle auf unterschiedlichen Abstraktionsebenen generiert.

Laut Poole [14] gibt es vier Abstraktionsebenen in der Model Driven Architecture, welche in Abbildung 1 visualisiert sind.

Abstraktionsebenen in der Model Driven Architecture nach Poole

Abbildung 1: Abstraktionsebenen in der Model Driven Architecture nach Poole

  1. Das Computational Independent Model (CIM), auch Business Model genannt, enthält keine Designrestriktionen bezüglich Technologie oder Plattform. Es beschreibt ausschließlich das grobe Konzept der Zielanwendung auf einem sehr hohen Niveau der Abstraktion.
  2. Das Platform Independent Model (PIM) enthält Details über mögliche Berechnungsvorgänge, ist aber ebenfalls noch nicht auf eine Technologie oder Plattform festgelegt.
  3. Das Platform Specific Model (PSM) enthält, wie der Name schon sagt, konkrete Details zur Implementierung bezüglich Technologie und Plattform.
  4. Der generierte Quellcode ist das Resultat der modellgetriebenen Entwicklung. Er wurde auf Basis des PSM in Kombination mit spezifischer Plattformkenntnis und Technologiekenntnis (beispielsweise
    Programmiersprache und Betriebssystem) generiert.

Wie die Konversationsprozesse zwischen den unterschiedlichen Modellen aussehen könnte, würde den Rahmen dieses Artikels sprengen und kann an anderer Stelle nachgelesen werden [16].

Herausforderungen bei der modellgetriebenen Softwareentwicklung

Neben der oben genannten Komplexität der Rückrechnung von bestehendem Code zu abstrakten Modellen, welche dann auch noch für Menschen lesbar und leicht verständlich sein sollen, gibt es noch weitere Herausforderungen.

Beispielsweise existiert im Bereich des Tooling nach wie vor eine Art Henne-Ei-Problem. Obwohl unterschiedliche Verfahren bereits recht fundiert erforscht sind, gibt es aufgrund mangelnder Nachfrage noch keine wirklich ausgereiften Tools für den Entwicklungsworkflow. Das Eclipse Modeling Framework [4] und das darauf basierende Tool Eclipse Papyrus [5] sind da eher die Ausnahmen und sind erste größtenteils ausgereifte integrierte Lösungen aus dem Open Source Bereich. Auch für einzelne Spezialgebiete, wie beispielsweise der Generierung von Domainklassen aus XML-Dateien gibt es zum Beispiel für Java Lösungen [12].

Ein weiteres Problem ist die große semantische Lücke zwischen abstraktem Modell und konkretem Quellcode, die aufgrund der unterschiedlichen Abstraktionsebenen entsteht [1]. Mit dem Einzug der Compiler konnte bereits eine semantische Lücke zwischen Quellcode und Maschinen Code gefüllt werden. Dabei waren die Compiler den Programmierern aufgrund ihrer effizienteren formalen Optimierungsprozesse in Bezug auf die Effizienz des Quellcodes massiv im Vorteil. Dafür können die meisten menschlichen Programmierer aus dem Maschinencode nicht mehr ablesen, was das Programm als Ganzes tut. Auch im Bereich der Rückrechnung (Decompiling) entstehen je nach Compiler und Programmiersprache manchmal interessante Konstrukte in der Hochsprache, die aufgrund der Optimierungen des Compilers entstanden sind und mit dem originalen Quellcode wenig zu tun haben.

Da die Lücke zwischen abstraktem Modell und konkretem Quellcode, wie oben erwähnt, weitaus breiter ist als bei der Quellcode-zu-Maschinencode-Konversation, werden die genannten Schwierigkeiten wahrscheinlich nur noch größer.

Auch die hohe Abstraktion kann insbesondere bei kleineren Softwareprojekten die Entwicklungsgeschwindigkeit aufgrund des höheren Aufwands eher verschlechtern als verbessern. Schließlich müssen jedes Domainobjekt und jeder Prozess formal ausführlich definiert werden. Damit wird durch die Abstraktion die Komplexität erhöht und nicht verringert.

Ein weiterer Punkt ist das implizite Wissen zwischen Menschen. In der Kommunikation zwischen den Auftraggebern und den Entwicklern gibt es meist eine große Menge impliziten Wissens. Damit soll in diesem Zusammenhang gar nicht mal von domainspezifischem Wissen die Rede sein, sondern auch eine große Menge trivialer Dinge, wie beispielsweise aus dem Geburtsdatum das Alter zu berechnen.

Vorteile bei der modellgetriebenen Softwareentwicklung

Neben all den Nachteilen gibt es natürlich auch Vorteile beim Einsatz der modellgetriebenen Softwareentwicklung. Beispielsweise ist die Dokumentation weniger aufwendig, da sie aufgrund der abstrakten Modelle bereits in Teilen als Artefakt im Entwicklungsprozess erstellt wird. Die daraus resultierende Dokumentation kann außerdem im Optimalfall niemals von der realen Implementierung abweichen, da die Modelle ja den Quellcode definieren.

Auch Code Hacking und die damit verbundene Verwaltung und Wartung unsauberen Spaghetticodes ist durch den ausschließlich generierten Code in der Theorie nicht erforderlich. Auch ist der generierte Code einheitlicher, was insbesondere bei Entwicklungsteams mit mehreren Entwicklern nicht leicht zu erreichen ist. Dadurch sollte die allgemeine Codequalität steigen. Auf der anderen Seite kann der Codegenerator vergleichbar mit einem Compiler aber auch Optimierungen am Quellcode durchführen, die für einen Menschen auf den ersten Blick unverständlich erscheinen, formal aber Sinn ergeben.

Einer der größten Vorteil ist außerdem, dass theoretisch ein Domainexperte das erforderliche Programm selbst erstellen kann und es damit nicht zu Kommunikationsfehlern zwischen dem Experten und dem Entwicklungsteam kommen kann. Das spart Entwicklungszeit und Entwicklungskosten. Jedoch müsste der Domainexperte dafür ausreichendes technisches Know-How und formales Denken mitbringen, um den doch eher formalen Modellierungsprozess überhaupt durchführen zu können.

Zusätzlich kann durch die Modelle und das höhere Abstraktionslevel die hohe Komplexität insbesondere von großen Anwendungen einfacher beherrscht werden, sofern die ausführlichere Spezifikation die Übersicht nicht beeinträchtigt.

Hinzu kommt die theoretische Plattformunabhängigkeit. Wenn das abstrakte CIM erst einmal erstellt ist, können problemlos Anwendungen für andere Systeme und Technologien generiert werden.

Wann ist modellgetriebene Softwareentwicklung sinnvoll?

Ein einfaches UML-Diagramm zu erstellen, geht schnell. Aber ein UML-Diagramm zu erstellen, welches sowohl sämtliches implizites Wissen explizit macht, als auch alle Randfälle beachtet und alle Domainobjekte umfangreich spezifiziert, macht doch deutlich mehr Aufwand.

Dieser durch die Abstraktion entstehende Mehraufwand ist nur dann sinnvoll, wenn er sich lohnt. In welchen Fällen könnte er sich also lohnen?

  1. Wenn ein bestimmtes Modul oder eine ganze Software einem bestimmten Industriestandard entsprechen muss. Wenn beispielsweise Interoperabilität mit anderer Software anderer Technologien über feste Schnittstellen gewährleistet sein muss, ist es sinnvoll, diese Schnittstellenmodule modellgetrieben auf Basis eines Schnittstellenmodells zu entwickeln. Dadurch ist nämlich eine zu 100% standardkonforme Lösung möglich. Derartige Anforderungen gibt es in der Softwareentwicklung für Produktionssysteme in Industrieunternehmen oder der Low-Level-Programmierung häufiger als in der allgemeinen Softwareentwicklung.
  2. Wenn ein festgelegtes Architekturkonzept durchgesetzt werden soll oder muss. Bei der manuellen Entwicklung kommt es dagegen häufig zu Abweichungen und das Artefakt enthält am Ende unterschiedliche Komponenten, denen unterschiedliche Architekturmodelle zugrunde liegen. Schließlich weiß aufgrund mangelnder Dokumentation niemand mehr, was am Ende wie gelöst wurde.
  3. Wenn die Anforderungen ohnehin bereits in einer formal-abstrakten Form widerspruchsfrei vorhanden sind, beispielsweise in Form von OCL-Statements [11].

Eine Übersicht über konkrete Use Cases für Model Driven Development und Use Case Stories von Eclipse Papyrus würden den Rahmen dieses Artikels sprengen und sind hier [3] zu finden.

In der Realität werden häufig Teile aus dem Gebiet der modellgetriebenen Softwareentwicklung verwendet, wie beispielsweise Code-Generierung (beispielsweise via JAXB) oder die Generierung von sogenannten „Skeletons“ auf Basis von unvollständigen UML-Diagrammen, die der Übersicht dienen. Diese Skeletons stellen beispielsweise Domainobjekte oder leere Methodenrumpfe dar, die anschließend von einem Programmierer ausgefüllt werden. Auch Datenbankmappings (beispielsweise für Hibernate) und persistente Klassen können einfach generiert werden.

Wenn wirklich modellgetrieben entwickelt werden soll, erfolgen meistens Kompromisse und Absprachen, wie die strikte modellgetriebene Entwicklung zugunsten der Produktivität etwas aufgeweicht werden kann.

Beispielsweise wird auf eine Rückrechnung des Modells auf Basis des Quellcodes verzichtet, die Automatisierung funktioniert ausschließlich von Modell zu Code. Zusätzlich sollten generierte Artefakte nicht verändert werden und im besten Fall nicht einmal in ein Versionskontrollsystem wie Git [6] eingecheckt werden. Wenn doch Änderungen vorgenommen werden sollen, können die geänderten Coderegionen über Schutzblöcke markiert werden, damit sie bei einer erneuten Generierung nicht überschrieben werden. Dies führt dann jedoch bei erneuter Generierung zu einer Reihe von Problemen. Besser ist es, den generierten Quellcode vom manuell erstellten Quellcode so gut es geht zu separieren und im Modell auf Stellen im manuell erstellten Quellcode zu verweisen.

Ein Ansatz ist es, das abstrakte Modell nicht komplett vollständig zu modellieren und damit nur abstrakte Oberklassen und Interfaces zu generieren. Diese Klassen werden dann vererbt und die konkrete Implementierung von Hand eingefügt. Damit wird Zeit beim Erstellen und Debuggen von Standardstrukturen durch den Entwickler eingespart.

Fazit

Der akademische Ansatz des Elaborationists inklusive Round-Trip-Engineering ist in der Praxis aufgrund mangelnden Toolings und der zu hohen semantischen Lücke quasi unbrauchbar.

Allgemein hat die modellgetriebene Softwareentwicklung mit dem Ansatz des Translationists durchaus seine Berechtigung im Industriebereich und in Projekten, in denen bestimmte Standards oder Schnittstellendefinitionen eingehalten werden sollen.

Jedoch werden selbst die dem modellgetriebenen Ansatz absolut abgewandten Entwickler zugeben müssen, dass der Versuch, den gemeinen Softwareentwickler abzuschaffen mit all seiner Dekadenz am Ende doch ein paar äußerst hilfreiche Codegeneratoren (wie beispielsweise JAXB) hervorgebracht hat, die sich nun in fast jedem Softwareprojekt sinnvoll nutzen lassen. Und wer malt schon die UML-Diagramme zur Dokumentation von Hand, wenn er sie sich aus dem fertigen Code erzeugen lassen kann?

Quellen

[1] Bran Selic. 2003. The pragmatics of model-driven development. IEEE Softw. 20, 5 (2003), 19–25.
[2] David Ameller. 2009. Considering Non-Functional Requirements in Model-Driven Engineering. (June 2009). Retrieved April 19, 2017 from http://www.essi.upc.edu/dameller/wp-content/papercite-data/pdf/ameller2009-master.pdf
[3] Eclipse Foundation. 2017. Papyrus Use Case Stories. Papyrus Use Case Stories. Retrieved from https://eclipse.org/papyrus/testimonials.html
[4] Eclipse Foundation. 2017. Eclipse Modeling Framework EMF. Eclipse Modeling Project. Retrieved from https://eclipse.org/modeling/emf/
[5] Eclipse Foundation. 2017. Papyrus. Eclipse. Retrieved from https://eclipse.org/papyrus/
[6] Git. 2017. Git. Git. Retrieved from https://git-scm.com/
[7] Gregory G. Nordstrom. 1998. Metamodeling – Rapid Design and Evolution of Domain-Specific Modeling Environments.
[8] Anneke G. Kleppe, Jos B. Warmer, and Wim Bast. 2003. MDA explained: the model driven architecture: practice and promise. Addison-Wesley, Boston.
[9] Stephen J. Mellor and Marc/Foreword By-Jacoboson Balcer. 2002. Executable UML: A Foundation for Model-Driven Architectures. Addison-Wesley Longman Publishing Co., Inc. Retrieved April 26, 2017 from http://dl.acm.org/citation.cfm?id=545976
[10] Object Management Group. 1997. UML Semantics version 1.1.
[11] Object Management Group. 2014. Object Constraint Language Version 2.4. Retrieved from http://www.omg.org/spec/OCL/2.4/PDF
[12] Oracle. 2017. JAXB. JAXB. Retrieved from http://www.oracle.com/technetwork/articles/javase/index-140168.html
[13] Pasi Kellokoski. 2000. Round-trip Engineering.
[14] John D. Poole. 2001. Model-driven architecture: Vision, standards and emerging technologies. In Workshop on Metamodeling and Adaptive Object Models, ECOOP. Retrieved March 9, 2017 from http://www.adaptiveobjectmodel.com/ECOOP2001/submissions/Model-Driven_Architecture.pdf
[15] Edwin Seidewitz. 2003. What models mean. IEEE Softw. 20, 5 (2003), 26–32.
[16] Steffen Jacobs. 2017. Utilizing Models and Model Driven Development in Industry 4.0 system engineering with OPC UA.

Short URL for this post: http://wp.me/p4nxik-2QN
This entry was posted in MDSD and tagged , , , , , , , . Bookmark the permalink.

One Response to Wann ist modellgetriebene Softwareentwicklung sinnvoll?

  1. Michael Hentze says:

    Interessanter Artikel, der sich mit meinen (bisherigen) Erfahrungen auf diesem Gebiet trifft.

Leave a Reply