Grundlagen der modellgetriebenen Softwareentwicklung Teil 4: Modelltransformation Prof. Dr. H. Drachenfels Hochschule Konstanz Version 10.0 26.1.2017 Modelltransformation: Klassifikation Modell-zu-Modell-Transformation (M2M): • Abbildung eines Quellmodells auf ein Zielmodell • Formulierung der Transformationsvorschriften auf der Ebene der Metamodelle • beide Metamodelle müssen das gleiche Meta-Metamodell haben erlaubt mehrstufige Transformationen erlaubt Anpassung von Modellen an neue DSL- und Metamodell-Versionen (Model Refactoring, Model Migration) Modell-zu-Text-Transformation (M2T): • Abbildung eines Quellmodells direkt auf eine textuelle Repräsentation des Zielmodells • für die Abbildung wird kein Ziel-Metamodell und damit auch kein gemeinsames Meta-Metamodell benötigt M2T ist der übliche Weg bei der Software-Generierung Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-1 Modelltransformation: prinzipieller Ablauf Frontend Quellmodell (konkreteSyntax) Instanz des Quell-Metamodells abstrahieren Quellmodell (abstrakte Syntax) Transformationsvorschriften API transformieren Application Programming Interface Zielmodell (abstrakte Syntax) Zielmodell (konkreteSyntax) Instanz des Ziel-Metamodells konkretisieren Backend Prof. Dr. H. Drachenfels Hochschule Konstanz 4-2 Grundlagen der modellgetriebenen Softwareentwicklung Modelltransformation: vereinfachter Ablauf Software-Generierung mit Templates (Modell-zu-Text-Transformation): Frontend Modell (konkreteSyntax) abstrahieren Modell (abstrakte Syntax) Templates Generierter Code (konkreteSyntax) Prof. Dr. H. Drachenfels Hochschule Konstanz API generieren Die Transformationsvorschriften liegen als Templates vor, d.h. als Code in konkreter Syntax, der an besonders markierten Stellen vom Generator vervollständigt wird. Grundlagen der modellgetriebenen Softwareentwicklung 4-3 M2M: Eigenschaften Aspekte für die Klassifikation von M2M-Transformationen: • Kardinalität 1:1 aus einem Quellmodell entsteht ein Zielmodell M:1 Zusammenfassung mehrere Quellmodelle zu einem Zielmodell • Richtung unidirektional bidirektional Quellmodell wird in Zielmodell überführt wechselseitige Synchronisation von Quell- und Zielmodell • Metamodell einheitlich verschieden Refactoring von Modellen echte Transformation (Meta-Metamodell ist einheitlich) • Modellzugriff erhaltend modifizierend Quellmodell wird nur gelesen, Zielmodell ist neue Instanz Quellmodell wird geändert, z.B. beim Refactoring Standardfall: unidirektionale, erhaltende 1:1-Transformation mit verschiedenem Metamodell Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-4 M2T: Eigenschaften Klassifikation von M2T-Transformationen: • Spezialgenerator mit interner Abbildungsvorschrift Der Generator gibt die Abbildung von Modellen auf Text fest vor. Änderung der Abbildung nur durch Umprogrammieren des Generators kein offen gelegtes API für den abstrakten Syntaxbaum Beispiel: Stub-Generatoren für den Fernaufruf von Methoden in verteilten Systemen • universeller Generator mit externer Abbildungsvorschrift Die Abbildung von Modellen auf Text ist in Templates ausgelagert. Änderung der Abbildung durch Verwendung anderer Templates offen gelegtes API für den abstrakten Syntaxbaum Templates werden mit einer Templatesprache formuliert Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-5 M2T: Modell – Programm – Plattform Modelle <<generiert aus>> Generierter Code Manueller Code Programm <<benötigt>> Plattform Prof. Dr. H. Drachenfels Hochschule Konstanz • Im Modellen enthaltene Aspekte eines Programms werden durch Generierung automatisch in Code überführt. (verhindert Inkonsistenzen durch vertikale Redundanz) • Im Modell nicht enthaltene Aspekte eines Programms werden manuell codiert. (zwischen generiertem Code und manuell erstelltem Code bestehen Abhängigkeiten, im Extremfall bis hin zu völliger Durchmischung) • Programme werden für eine Plattform erstellt. Grundlagen der modellgetriebenen Softwareentwicklung 4-6 Plattform: Begriffsdefinition klassischer Plattform-Begriff Plattform von einer Software als gegeben vorausgesetzte Umgebung, typischerweise bestehend aus Hardware-Architektur, Betriebsystem und Laufzeitbibliotheken Eine Software wird dementsprechend oft als plattform-unabhängig bezeichnet, wenn sie auf unterschiedlichen Betriebssystemen und / oder Hardwarearchitekturen lauffähig ist. vereinfachter Plattform-Begriff (hinreichend im Umfeld Modellgetriebene Softwareentwicklung) Software-Plattform System von APIs (Application Programming Interfaces), das eine Anwendung benötigt, um ablauffähig zu sein. Die Bezeichnungen Plattform und Anwendung sind dabei relativ. Eine Anwendung kann ihrerseits wieder Plattform für Code einer nächst höheren Abstraktionsebene sein. Zu den APIs können Systemaufrufe des Betriebssystems, die Schnittstellen von Standardsoftware (z.B. Datenbanksystem), die Laufzeitbibliothek der Programmiersprache, domänenspezifische Frameworks usw. gehören. Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-7 Plattform: Abstraktionsebenen Unterscheidung von Plattformen nach Abstraktionsebene: • elementare Software-Plattform nur Betriebssystem mit etwas Standardsoftware und der Laufzeitbibliothek der Programmiersprache Art des generierten Codes: normaler Programmcode Umfang des generierten Codes: vergleichsweise groß Generierung erlaubt es, statt komplizierter Abstraktionsmechanismen eine einfache Softwarearchitektur mit viel horizontaler Redundanz zu wählen. • komplexe Software-Plattform Verwendung domänenspezifischer Frameworks Art des generierten Codes: vorrangig Konfiguration Umfang des generierten Codes: vergleichsweise gering Generierung vereinfacht die Konfiguration der Frameworks und erleichtert die Migration auf andere Frameworks Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-8 Plattform: Verwendungszweck Unterscheidung von Plattformen nach Verwendungszweck: • Zielplattform: darauf soll das fertige Programm letztendlich produktiv laufen • Entwicklungs- und Testplattform: vorläufiger Ersatz für die Zielplattform Gründe für eine Entwicklungs- und Testplattform: • Zielplattform wird auch erst entwickelt Abgrenzung zwischen Anwendungs- und Plattformcode dann mitunter unscharf • Zielplattform nur eingeschränkt verfügbar z.B. weil Hardware oder Softwarelizenzen nur in geringer Stückzahl vorhanden sind • Zielplattform mühsam zu benutzen z.B. lange Compile-Load-Run-Zyklen bei Spezialhardware oder eingeschränkte Debug-Möglichkeiten • Zielplattform ungeeignet für Komponententests (Unit-Tests) z.B. weil ein Testframework verwendet werden soll Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-9 Generierter und manueller Code: Abhängigkeiten • Generierter und manueller Code in getrennten Übersetzungseinheiten: Abhängigkeiten entstehen durch die wechselseitige Verwendung von Namen (z.B. Variablenzugriffe, Methodenaufrufe, Typinstanzierungen, ...) kein prinzipieller Unterschied zu den Abhängigkeiten vom Plattform-Code zur Entschärfung der Abhängigkeiten allgemeine Regeln zur losen Kopplung beachten • Generierter und manueller Code in einer Übersetzungseinheit gemischt: Abhängigkeiten entstehen durch die Einbettung (z.B. in generierter Methode Implementierung ganz oder teilweise manuell, ...) manuelle Teile gehen bei erneutem Generierungslauf verloren, wenn sie nicht besonders geschützt werden deshalb vermischten Code möglichst vermeiden Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-10 Generierter und manueller Code: Präprozessortechnik Präprozessortechnik trennt Codeanteile auf Dateiebene • generierten und manuellen Code in getrennten Dateien erstellen und per Präprozessor zu übersetzbarem Code kombinieren z.B. Einbettung manuellen Codes in generierten Code mittels C++-Präprozessor (umgekehrte Einbettung von generiertem in manuellen Code funktioniert analog): // GenerierteKlasse.cpp #include "GenerierteKlasse.h" ... void GenerierteKlasse::GenerierteMethode() { #include "manuelle_methodenimplementierung.h" } Include-Datei bleibt bei Neugenerierung erhalten ... und kann in Versionsverwaltung gepflegt werden • Dateiaufteilung führt tendenziell zu schwer lesbarem unübersichtlichem Code, erleichtert aber die Versions- und Konfigurationsverwaltung Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-11 Generierter und manueller Code: Entwurfsmuster Objektorientierte Entwurfsmuster trennen Codeanteile auf Klassenebene • abstrakte Oberklasse (oder ein Interface) generieren und abstrakte Methoden in konkreter Unterklasse manuell implementieren (oder auch umgekehrt) // GenerierteKlasse.java public abstract class GenerierteKlasse { ... public abstract void generierteMethode(); ... Unterklasse bleibt bei Neugenerierung der Oberklasse erhalten } und kann separat in Versionsverwaltung gepflegt werden // ManuelleKlasse.java public final class ManuelleKlasse extends GenerierteKlasse { public void generierteMethode() { ... // Implementierung } } komplexere Vermischungen von generiertem und manuellem Code können mit dem Template-Entwurfsmuster entflochten werden Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-12 Generierter und manueller Code: geschützte Bereiche Geschützte Bereiche erlauben Einbettung von manuellem in generierten Code • im generierten Code den manuellen Code mit speziellen generatorspezifischen Kommentaren markieren, damit er bei Neugenerierungen übernommen wird // GenerierteKlasse.java eindeutige Kennung public class GenerierteKlasse { ... des Modellelements void generierteMethode() { // PROTECTED REGION ID(123456) START ... // manueller Code // PROTECTED REGION END } ... } • auch geschützter manueller Code kann bei Neugenerierungen verloren gehen z.B. kann nach dem Umbenennen oder Löschen von Modellelementen der geschützte manuelle Code eventuell nicht mehr zugeordnet werden • geschützte Bereiche erschweren die Versions- und Konfigurationsverwaltung Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-13 Generierter und manueller Code: Templates Codetemplates erlauben Einbettung von generiertem in manuellen Code • in manuellen Code spezielle generatorspezifische Anweisungen einbetten, die den generierten Code einfügen // ${entity.name}.java public class ${entity.name} extends ManuelleKlasse { ... #foreach($attribute in $entity.attributes) public ${attribute.type} get${attribute.name}() { return this.${attribute.name}; } private ${attribute.type} ${attribute.name}; #end ... } • aktuell die bevorzugte Technik für das Mischen der beiden Codeanteile Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-14 Generierter und manueller Code: Modellanreicherung Modellanreicherung erlaubt Einbettung von manuellem Code auf Modellebene • manuellen Code in das Modell einbetten statt in den daraus generierten Code z.B. beim Scanner-Generator flex und dem Parser-Generator bison wird die Grammatikspezifikation um C-Code angereichert • vermeidet im Vergleich zu geschützten Bereichen die Probleme mit der Versions- und Konfigurationsverwaltung • aber führt tendenziell zu schwer lesbaren unübersichtlichen Modellen • beeinträchtigt die Plattformunabhängigkeit des Modells durch den eingebetteten Code legt ein angereichertes Modell bereits die Programmiersprache und einige benötigte APIs fest Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-15 Transformationssprachen Templatesprachen beschreiben die Einbettung von Modellelementen in Zielcode • bieten Sprachmittel für den Zugriff auf den abstrakten Syntaxbaum des Modells • erlauben auch Transformationen in nicht-formale Sprachen • Templates ähneln weitgehend dem zu generierenden Code und lassen sich deshalb leicht aus Beispielcode ableiten Beispiele: VTL (Velocity Template Language), StringTemplate M2M-Sprachen (Relationssprachen) beschreiben Relationen zwischen Elementen der Quellsprache und Elementen der Zielsprache • erlauben das Formulieren von Regeln für die Abbildung von Knoten des Quell-AST auf Knoten des Ziel-AST • verlangen in der Regel eine gemeinsames Meta-Metamodell für Quelle und Ziel Beispiele: ATL (Atlas Transformation Language), XSLT Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-16 Templatesprachen: VTL VTL ist die Templatesprache des Apache Velocity Projekts. • Referenzen erlauben den Zugriff auf Java-Objekte eines Modells: $name.property $name.method(Parameter) Die Objekte werden Java-seitig in Form einer Hashtabelle bereitgestellt. Schlüssel ist ein String name, der dann im Template als $name angegeben wird. Properties werden auf getter-Methoden abgebildet. Modellzugriffe liefern als Ergebnis immer einen String. • Iteration über Modellelemente: #foreach( $i in Referenz ) ... #end • Bedingte Template-Abschnitte: #if( Bedingung ) ... #elseif( Bedingung ) ... #else ... #end • Aufteilung und Gliederung von Templates: unter anderem mit #include(...), #parse(...), #macro(...) ... #end Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-17 Templatesprachen: StringTemplate StringTemplate ist die Templatesprache des ANTLR Parser Generators, ist aber unabhängig von ANTLR verwendbar. • Referenzen erlauben den Zugriff auf Java-Objekte eines Modells: <attribute.property> Attribute sind sozusagen Parameter von Templates und werden Java-seitig mit Objekten des Modells initialisiert. Properties werden auf getter-Methoden abgebildet. • funktionaler Stil für die Iteration über Modellelemente: <attribute.property:Template()> <attribute.property:Template(); separator=”…”> Auf alle Elemente der Property das angegebene Template anwenden. • Bedingte Template-Abschnitte: <if( Bedingung )> ... <elseif( Bedingung )> ... <else> ... <end> Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-18 Beispiel: Codegenerierung für Joi (1) Die Sprache Joi (Java Objects by Interface) erlaubt die Modellierung von "Komponenten im Kleinen", d.h. von Klassen, • die nicht per Vererbung erweitert werden können • deren Instanzen nur per Fabrikmethode erzeugt werden können • deren Instanzen nur per Schnittstellenreferenz benutzt werden können Konkrete Syntax (vereinfacht) in EBNF: "component" Name "implements" Schnittstelle {"," Schnittstelle} "{" {Methodenimplementierung} {Datenfeld} "}" Abstrakte Syntax (vereinfacht) als Metamodell: Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-19 Beispiel: Codegenerierung für Joi (2) • Beispielmodell HelloWorld.joi (konkrete Syntax): component HelloWorld implements Hello, Goodbye { @method void sayHello( ) { Die Schnittstellen werden ganz normal System.out.println("Hello world!"); in Java programmiert: hello = true; public interface Hello { } void sayHello(); @end } @method public interface Goodbye { void sayGoodbye( ) { void sayGoodbye(); if (!hello) sayHello(); System.out.println("Goodbye world!"); } } @end @field boolean hello; @end } Prof. Dr. H. Drachenfels Hochschule Konstanz Die Methodenimplementierungen und Felddefinitionen sind als manueller Java-Code in das Modell eingebettet (Modellanreicherung) 4-20 Grundlagen der modellgetriebenen Softwareentwicklung Beispiel: Codegenerierung für Joi (3) • Beispielmodell als UML Objektdiagramm (abstrakte Syntax) :JoiMethod code = "void sayHello( ) { ... }" :JoiMethod :JoiInterface name = "Hello" :JoiInterface code = "void sayGoodbye( ) { ... }" :JoiComponent name = "HelloWorld" name = "Goodbye" :JoiField code = "boolean hello;" Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-21 Beispiel: Codegenerierung für Joi (4) • Beispielmodell mit manuellem Java-Code instanziiert (abstrakte Syntax): JoiComponent c = new JoiComponent( bei Verwendung einer DSL "HelloWorld", übernimmt der Parser die new JoiInterface[] { Instanziierung new JoiInterface("Hello"), new JoiInterface("Goodbye") }, new JoiMethod[] { new JoiMethod("void sayHello( ) { ... }"), new JoiMethod("void sayGoodbye( ) { ... }") }, new JoiField[] { new JoiField("boolean hello;") } ); Prof. Dr. H. Drachenfels Hochschule Konstanz 4-22 Grundlagen der modellgetriebenen Softwareentwicklung Beispiel: Codegenerierung für Joi (5) • zu generierende Java-Realisierung HelloWorld.java des Beispielmodells: public final class HelloWorld { private HelloWorld() { } public static Implementation getInstance() { return new Implementation(); } Fabrikmethode private static final class Implementation implements Hello, Goodbye { public void sayHello( ) { ... } gekapselte Implementierung public void sayGoodbye( ) { ... } private boolean hello; } } Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-23 Beispiel: Codegenerierung für Joi mit Velocity (1) • Transformationstemplate joi2java.vtl: ## joi2java.vtl public final class $component.name { private ${component.name}() { } public static Implementaion getInstance() { Fabrikmethode return new Implementation(); } private static final class Implementation implements #foreach($i in $component.interfaces) Schnittstellen #if($velocityCount > 1) , #end $i.name #end { #foreach($m in $component.methods) Methodenimplementierungen public $m.code #end #foreach($f in $component.fields) private $f.code Felddefinitionen #end } } Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-24 Beispiel: Codegenerierung für Joi mit Velocity (2) • Codegenerator JoiToJava.java: ... // import-Anweisungen für org.apache.velocity- und java.io-Klassen public final class JoiToJava { private JoiToJava() { } public static void main(String[] args) throws Exception { // AST für HelloWorld instanziieren: JoiComponent ast = ... // Template anwenden: Writer writer = new OutputStreamWriter(System.out); VelocityContext context = new VelocityContext(); context.put("component", ast); VelocityEngine velocity = new VelocityEngine(); velocity.init(); velocity.mergeTemplate("joi2java.vtl", context, writer); writer.close(); } } Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-25 Beispiel: Codegenerierung für Joi mit Stringtemplate (1) • Transformationstemplate joi2java.stg: component(c) ::= << public final class <c.name> { private <c.name>() { } public static Implementation getInstance() { return new Implementation(); } private static final class Implementation implements <c.interfaces:interface(); separator=", "> { <c.methods:method(); separator="\n\n"> <c.fields:field(); separator="\n"> } } wendet das Template field (siehe nächste Seite) >> auf alle fields der Komponente c an, ... nach jedem field wird ein Zeilenwechsel eingefügt Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-26 Beispiel: Codegenerierung für Joi mit Stringtemplate (2) ... Schnittstelle interface(i) ::= "<i.name>" method(m) ::= << public <m.code> Methodenimplementierung >> field(f) ::= << private <f.code> Felddefinition >> • Codegenerator: ... JoiComponent ast = ... // AST für HelloWorld instanziieren ST templ = new STGroupFile("joi2java.stg") .getInstanceOf("component"); templ.add("c", ast); System.out.println(templ.render()); ... Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-27 M2M-Sprachen: XSLT XSLT ist eine M2M-Sprache für die Umwandlung von XML-Dokumenten in andere XML-Dokumente (oder auch sonstige Textformate). • Templates werden auf Knoten im DOM-Tree angewendet: <xsl:template match="XPath-Ausdruck"> ... </xsl:template> • funktionaler Stil für die Iteration über Kindknoten im DOM-Tree: <apply-templates/> XPath ist eine Sprache für die Navigation in DOM-Trees. • XPath-Ausdrücke bestehen aus einer durch Schrägstriche '/' getrennten Folge von Lokalisierungsschritten: axis::nodetest[predicate] Achsen sind u.a. self descendant ancestor following preceeding Prof. Dr. H. Drachenfels Hochschule Konstanz der aktuelle Knoten alle Knoten unterhalb im Baum alle Knoten oberhalb im Baum alle Knoten rechts im Baum = unterhalb im Dokument alle Knoten links im Baum = oberhalb im Dokument 4-28 Grundlagen der modellgetriebenen Softwareentwicklung Beispiel: M2M-Transformation für Joi (1) • M2M-Transformationsregeln werden auf Ebene der Metamodelle fomuliert: Quellmetamodell für Joi Zielmetamodell für die Java-Realisierung von Joi JavaMethod JavaInterface +name : String JavaClass JavaField Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-29 Beispiel: M2M-Transformation für Joi (2) • Beispiel-Quellmodell in abstrakter Syntax :JoiMethod code = "void sayHello( ) { ... }" :JoiMethod :JoiInterface name = "Hello" :JoiInterface code = "void sayGoodbye( ) { ... }" :JoiComponent name = "HelloWorld" name = "Goodbye" :JoiField code = "boolean hello;" Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-30 Beispiel: M2M-Transformation für Joi (3) • Beispielmodell mit java.beans.XMLEncoder serialisiert (abstrakte Syntax): <?xml version="1.0" encoding="UTF-8"?> <java version="1.8.0_66" class="java.beans.XMLDecoder"> <object class="JoiComponent"> <string>HelloWorld</string> <array class="JoiInterface" length="2"> <void index="0"> <object class="JoiInterface"> <string>Hello</string> </object> </void> <void index="1"> <object class="JoiInterface"> <string>Goodbye</string> </object> </void> </array> ... Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-31 Beispiel: M2M-Transformation für Joi (4) ... <array class="JoiMethod" length="2"> <void index="0"> <object class="JoiMethod"> <string>void sayHello( ) { ... }</string> </object> </void> <void index="1"> <object class="JoiMethod"> <string>void sayGoodbye( ) { ... }</string> </object> </void> </array> <array class="JoiField" length="1"> <void index="0"> <object class="JoiField"> <string>boolean hello;</string> </object> </void> </array> </object> </java> Prof. Dr. H. Drachenfels Hochschule Konstanz 4-32 Grundlagen der modellgetriebenen Softwareentwicklung Beispiel: M2M-Transformation für Joi (5) • Beispiel-Zielmodell in abstrakter Syntax :JavaMethod modifier = "private" code = "HelloWorld( ) { }" :JavaClass modifier = "public final" name = "HelloWorld" e :JavaMethod modifier = "public static" code = "Implementation getInstance( ) {...}" :JavaMethod :JavaInterface name = "Hello" :JavaClass modifier = "private static final" modifier = "public" code = "void sayHello( ) {...}" name = "Implementation" :JavaInterface name = "Goodbye" :JavaMethod modifier = "public" code = "void sayGoodbye( ) {...}" :JavaField modifier = "private" code = "boolean hello;" Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-33 Beispiel: M2M-Transformation für Joi (6) • Beispiel-Zielmodell mit java.beans.XMLEncoder serialisiert (abstrakte Syntax): <?xml version="1.0" encoding="UTF-8"?> <java version="1.8.0_66" class="java.beans.XMLDecoder"> <object class="JavaClass"> <string>public final</string><string>HelloWorld</string> <array class="JavaInterface" length="0"/> <array class="JavaMethod" length="2"> <void index="0"> <object class="JavaMethod"> <string>private</string><string>HelloWorld( ) { }</string> </object> </void> <void index="1"> <object class="JavaMethod"> <string>public static</string> <string>Implementation getInstance( ) { ... }</string> </object> </void> </array> <array class="JavaField" length="0"/> ... Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-34 Beispiel: M2M-Transformation für Joi (7) ... <array class="JavaClass" length="1"> <void index="0"> <object class="JavaClass"> <string>private static final</string><string>Implementation</string> <array class="JavaInterface" length="2"> ... </array> <array class="JavaMethod" length="2"> <void index="0"> <object class="JavaMethod"> <string>public</string><string>void sayHello( ) { ... }</string> </object> </void> ... </array> <array class="JavaField" length="1"> ... </array> <array class="JavaClass" length="0"/> </object> </void> </array> </object> </java> Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-35 Beispiel: M2M-Transformation für Joi mit XSLT (1) • die XML-Serialisierung des Quell- und Zielmodells ermöglicht eine M2M-Transformation mittels XSLT • XSLT-Transformation am besten in mehrere Schritte aufteilen, beim Joi-Beispiel z.B. wie folgt: schritt1.xsl Klassennamen des Quellmetamodells JoiXXX durch Klassennamen des Zielmetamodells JavaXXX ersetzen schritt2.xsl überall das modifier-Attribut einfügen schritt3.xsl Struktur mit äußerer Komponenten-Klasse und eingebetter Implementation-Klasse erzeugen Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-36 Beispiel: M2M-Transformation für Joi mit XSLT (2) • XSLT-Transformation schritt1.xsl: <?xml version="1.0" encoding="iso-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="node()|@*"> <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy> </xsl:template> <xsl:template match="@class[.='JoiComponent']"> <xsl:attribute name="class">JavaClass</xsl:attribute> </xsl:template> <xsl:template match="@class[.='JoiInterface']"> <xsl:attribute name="class">JavaInterface</xsl:attribute> </xsl:template> <xsl:template match="@class[.='JoiField']"> <xsl:attribute name="class">JavaField</xsl:attribute> </xsl:template> <xsl:template match="@class[.='JoiMethod']"> <xsl:attribute name="class">JavaMethod</xsl:attribute> </xsl:template> </xsl:stylesheet> Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-37 Beispiel: M2M-Transformation für Joi mit XSLT (3) • XSLT-Transformation schritt2.xsl (graue Teile wie schritt1.xsl): ... <xsl:template match="string"> <xsl:choose> <xsl:when test="../@class='JavaClass'"> <string></string> <xsl:copy-of select="." /> </xsl:when> <xsl:when test="../@class='JavaInterface'"> <xsl:copy-of select="." /> </xsl:when> <xsl:when test="../@class='JavaMethod'"> <string>public</string> <xsl:copy-of select="." /> </xsl:when> <xsl:when test="../@class='JavaField'"> <string>private</string> <xsl:copy-of select="." /> </xsl:when> </xsl:choose> </xsl:template> ... Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-38 Beispiel: M2M-Transformation für Joi mit XSLT (4) • XSLT-Transformation schritt2.xsl (graue Teile wie schritt1.xsl): ... <xsl:template match="/java/object"> Name der Komponente <object class="JavaClass"> <string>public final</string> <string><xsl:value-of select="string[2]" /></string> <array class="JavaInterface" length="0" /> <array class="JavaMethod" length="2"> <void index="0"> <object class="JavaMethod"> <string>private</string> <string><xsl:value-of select="string[2]" />( ) { }</string> </object> </void> <void index="1"> <object class=" JavaMethod"> <string>public static</string> <string>Implementation getInstance( ) { ... }</string> </object> </void> </array> ... Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-39 Beispiel: M2M-Transformation für Joi mit XSLT (5) ... <array class="JavaField" length="0" /> <array class="JavaClass" length="1"> <void index="0"> <object class="JavaClass"> <string>private static final</string> <string>Implementation</string> Interfaces, Methoden und Felder der Komponente <xsl:copy-of select="array" /> <array class="JavaClass" length="0" /> </object> </void> </array> </object> </xsl:template> ... Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-40 Beispiel: M2M-Transformation für Joi mit XSLT (6) • Transformator JoiToJava.java: ... // import-Anweisungen für javax.xml.transform-Klassen usw. public final class JoiToJava { private JoiToJava() { } public static void main(String[] args) throws Exception { Source[] xslt = new Source[] { new StreamSource(Files.newInputStream(Paths.get("schritt1.xsl"))), new StreamSource(Files.newInputStream(Paths.get("schritt2.xsl"))), new StreamSource(Files.newInputStream(Paths.get("schritt3.xsl"))) }; StreamSource source = ... // XML-serialisiertes Quellmodell StreamResult result = ... TransformerFactory factory = TransformerFactory.newInstance(); for (Source transformation: xslt) { factory.newTransformer(transformation).transform(source, result); ... // result als source für nächsten Schritt setzen } ... // XML-serialisiertes Zielmodell ausgeben } Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-41 Beispiel: M2M-Transformation für Joi mit ATL (1) • ATL Transformationsregeln Joi2Java.atl: module Joi2Java; create OUT: Java from IN: Joi; -- Namen der Metamodelle rule JoiComponent2Class { -- "Matched Rule" für Komponenten from f : Joi!JoiComponent to t : Java!Class ( modifier <- 'public final ', name <- f.name, methods <- Sequence{tc,tf}, classes <- Sequence{ti} ), ... Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-42 Beispiel: M2M-Transformation mit ATL (2) -- Fortsetzung "Matched Rule" für Komponenten tc : Java!Method ( modifier <- 'private', code <- f.name + '( ) { }' ), tf : Java!Method ( modifier <- 'public', code <- 'Implementation getInstance( ) { return new Implementation( ); }' ), ti : Java!Class ( modifier <- 'private static final', name <- 'Implementation ', interfaces <- f.interfaces, methods <- f.methods, fields <- f.fields ) } ... Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-43 Beispiel: M2M-Transformation mit ATL (3) ... rule JoiInterface2Interface { from f : Joi!JoiInterface to t : Java!Interface ( name <- f.name ) } rule JoiMethod2Method { from f : Joi!JoiMethod to t : Java!Method ( modifier <- 'public', code <- f.code ) } ... Prof. Dr. H. Drachenfels Hochschule Konstanz ... rule JoiField2Field { from f : Joi!JoiField to t : Java!Field ( modifier <- 'private', code <- f.code ) } Grundlagen der modellgetriebenen Softwareentwicklung 4-44 Generieren oder manuell erstellen? Grundsätzlich alles generieren, was sich aus dem Modell herleiten lässt und in der gewählten Plattform nicht bereits abgedeckt ist • aber Modell nicht um der Generierung willen aufblähen bei Modellen auf klar umrissene Domänen und auf Plattformunabhängigkeit achten • entweder generieren, oder manuell erstellen generierten Code möglichst nicht manuell nachbearbeiten • gut lesbaren Code generieren in der Regel die gleichen Qualitätskriterien erfüllen wie bei manuell erstelltem Code, z.B. Formatierungsregeln einhalten, Entwurfsmuster einsetzen • nicht nur für Zielplattform, sondern auch für Testplattform(en) generieren z.B. Mocks und Dummies für Komponententests generieren • nicht nur Programmcode generieren z.B. Dokumentation generieren, z.B. Skripte für Buildprozess, Deployment, Datenmigration usw. generieren Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-45 Generieren oder wiederverwenden? Grundsätzlich nichts generieren, was es bereits fertig zum wiederverwenden gibt • wiederverwendeten Code in Plattform integrieren bei Wiederverwendung von Komponenten braucht oft nur noch einfacher Glue-Code generiert zu werden • aber Plattform auch nicht zu sehr aufblähen oft wird nur ein Bruchteil der Funktionalität einer wiederverwendeten Komponente tatsächlich genutzt und der Rest als toter Code mitgeschleppt (Rattenschwanzeffekt) • auch Generierungen wiederverwenden, d.h. die Generierung kaskadieren z.B. aus dem Modell einer verteilten Anwendung im 1. Schritt IDL-Beschreibungen generieren und daraus im 2. Schritt per wiederverwendetem Generator die Client- / Server-Stubs generieren Prof. Dr. H. Drachenfels Hochschule Konstanz Grundlagen der modellgetriebenen Softwareentwicklung 4-46