MGSE
Modellgetriebene Softwareentwicklung - HTWG
Modellgetriebene Softwareentwicklung - HTWG
-
- 1 / 138
-
Flashcards
Externe DSL: Parser-Generator bison
bison generiert einen Parser, der Tokenfolgen auf syntaktische Korrektheit prüft
Externe DSL: Parser-Generator ANTLR
ANTLR 4 (Another Tool for Language Recognition Version 4) generiert Klassen für
die Erkennung von Tokens (Lexer statt Scanner genannt) und die Syntaxanalyse.
Aus einer Grammatikbeschreibung Name.g4 entstehen im Falle von Java:
NameLexer.java
NameParser.java
NameListener.java
NameBaseListener.java
Anders als bei flex und bison (und älteren Versionen von ANTLR) soll die
Grammatikbeschreibung keine Aktionen mit Programmcode enthalten.
Die Aktionen sollen statt dessen getrennt von der Grammatik in einer
Listener-Implementierung bereitgestellt werden (alternativ auch Visitor-Implementierung).
Empfehlungen DSLs, XML-DSL und UML-DSL
• Modelle bevorzugt mit DSLs formulieren!
Modellerstellung wird kompakter, weniger fehleranfällig und für Domänenexperten verständlicher
aber: die Entwicklung einer DSL kann je nach Sprachklasse sehr aufwendig sein
• Graphische oder textuelle DSL?
wenn vor allem komplexe Beziehungen zu modellieren sind, ist eine Graphik tendenziell anschaulicher Werkzeugunterstützung für Text ist wesentlich einfacher
vor der Entscheidung für die eine oder andere konkrete Syntax sollten erst einmal abstrakte Syntax und statische Semantik entworfen werden
• UML-Spezialisierung als DSL?
- Profile durch UML-Werkzeuge nicht wirklich gut unterstützt
- Einschränkung der Mächtigkeit von UML mittels OCL sehr mühsam
Profile sind eher geeignet, UML-Modelle mit Annotationen anzureichern
(wir kommen darauf bei den Modelltransformationen zurück)
• XML-Spezialisierung als DSL?
- wenig Spielraum beim Sprachdesign
- großer syntaktischer Ballast
+ sehr gute und reichhaltige Werkzeugunterstützung vorhanden
XML-Dialekte sind eher als Datenaustausch- und Speicherungsformat geeignet,
wegen des syntaktischen Ballasts nur in einfachen Fällen menschenlesbar/schreibar
Empfehlungen für interne und externe DSL
• interne DSL?
- wenig Spielraum beim Sprachdesign
- beschränkt auf Zielgruppe Programmierer, die die Wirtssprache beherrschen
+ geringer Implementierungsaufwand durch Wiederverwendung der Werkzeuge
der Wirtssprache
+ geringer Lernaufwand für die Zielgruppe
• externe DSL?
+ maximaler Spielraum beim Sprachdesign
- Implementierungs-, Pflege- und Lernaufwand kann sehr groß werden
deshalb eher für kleine Domänen geeignet
erstrebenswert: Komposition von DSLs aus wiederverwendeten Teil-DSLs
Was fpr Modelltransformation gibt es?
- Modell-zu-Modell-Transformation
- Modell-zu-Text-Transformation
Was ist eine Modell-zu-Modell-Transformation?
• 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)
Was ist Modell-zu-Text-Transformation?
• 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
Modelltransforomation - prinzipieller Ablauf
| Quellmodell [konkrete Synatax] | ----> (abstrahieren)
|------> | Quellmodell (abstrakte Syntax) |
| Transformationsvorschrift | ------------------------> (transformieren) <-----------|
API |-------------> | Zielmodell (abstrakte Syntax) |
(konkretisieren) <-----------|
|----------> | Zielmodell (konkrete Syntax)
Modelltransformation: vereinfachter Ablauf
|Modell (konkrete Syntax) | --> (abstrahieren) --> |Modell (abstrakte Syntax) | ----> (generieren)
Template >------| |----> |Generierte Code (konkr. Syntax)|
Model-to-Model-Transformation - 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 Quellmodell wird in Zielmodell überführt
bidirektional wechselseitige Synchronisation von Quell- und Zielmodell
• Metamodell
einheitlich Refactoring von Modellen
verschieden echte Transformation (Meta-Metamodell ist einheitlich)
• Modellzugriff
erhaltend Quellmodell wird nur gelesen, Zielmodell ist neue Instanz
modifizierend Quellmodell wird geändert, z.B. beim Refactoring
Standardfall: unidirektionale, erhaltende 1:1-Transformation mit verschiedenem Metamodell
Modell-to-Text Transformation - 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
Begriffsdefinition zu Plattform
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.
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.
Abstraktionsebenen von Plattformen
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 Framework und erleichtert die Migration auf andere Frameworks
Verwendungszwecke von Plattformen
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
Abhängigkeiten von generierter und manueller Code
• 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
Präprozessortechnik bei generierter und manueller Code
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"
}
...
• Dateiaufteilung führt tendenziell zu schwer lesbarem unübersichtlichem Code,
erleichtert aber die Versions- und Konfigurationsverwaltung
Entwurfsmuster in generierter und manueller Code
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();
...
}
// ManuelleKlasse.java
public final class ManuelleKlasse extends GenerierteKlasse {
public void generierteMethode() {
... // Implementierung
}
}
Geschützte Bereiche in generierter und manueller Code
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
public class GenerierteKlasse {
...
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
Templates in generierter und manueller Code
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
Modellanreicherung in generierter und manueller Code
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
Was sind Templatesprachen?
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
Was sind M2M-Sprachen?
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
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
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>
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
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 der aktuelle Knoten
descendant alle Knoten unterhalb im Baum
ancestor alle Knoten oberhalb im Baum
following alle Knoten rechts im Baum = unterhalb im Dokument
preceeding alle Knoten links im Baum = oberhalb im Dokument
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
Generieren oder wiederverwenden?
• 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
Modelgetriebene Softwareentwicklung - Motivation und Idee
• Motivation
Software wird größtenteils manuell geschrieben
Verwendung von Frameworks führt zu einer großen Menge
strukturähnlichen Codes
• Idee
Beschreibung des zu erzeugenden Systems durch ein Modell
Die Ausdrucksmöglichkeiten des Modells hängen von der
speziellen Domäne ab
Die domänenspezifische Sprache (DSL) beschreibt variable Teile
des Systems/Codes
In der DSL formulierte Modelle beschreiben die Varianten
DSL-Editoren ermöglichen einfache Erzeugung von Modellen
Generator generiert Artefakte aus Modellen
Vorteile von Modellgetriebene Softwareentwicklung
- Produktivitätssteigerung:
Entwicklungsaufwand verringert sich erheblich
- Komplexität wird verborgen:
Abstraktion vom verwendeten Framework | Rollenteilung zwischen fachlicher und nichtfunktionaler Entwicklung möglich
- Höhere Qualität, weniger Fehler:
Die Fehlerzahl im schematischen Code wird durch die Generierung erheblich reduziert. Fehler im generierten Code werden zentral im Generator behoben.
- Größere Flexibilität für Technologiewechsel und Produktvarianten
-- Technologiewandel
-- Produktfamilie
- Gute Dokumentation
Das Modell ist Kernbestandteil der Entwicklung. Er ist immer aktuell und veraltet nicht bei Änderungen des Codes.
Die Businesskonzepte sind vollständig dokumentiert. Modell abstrahieren von technologiscchen Abhängigkeiten und sind oft auch besser für die Domänenexperten verständlich.
Vorteile von MDSD
Vorteile:
1. Produktivitätssteigerung
2. Komplexität wird verborgen
3. Höhere Qualität
4. Flexibilität für Technologiewechsel und Produktvarianten
5. Gute Dokumentation
Nachteile von MDSD
Nachteile
• Kenntisse in Metamodellierung und
Modellierung nötig
• Entwicklung und/oder Nutzung eines
Toolsets erforderlich (DSL-Editoren)
• Entwicklung einer entsprechenden
Architektur incl. Generator nötig
• Innovationswille der Beteiligten
erforderlich
• Versionierung von Modellen nicht ganz
unproblematisch
DSL
Sprache, die die fachliche Domäne beschreibt (oft auch Metamodell)
Generator
Tool zur automatischen Erzeugung von Artefakten aus Modellen
Generat
Vom Generator erzeugtes Artefakt, z.B. Programm-Code oder Konfigurationsdateien
Template
Schablonenhafte Beschreibung des Generierungsvorgangs
Cartridge
DSL, Generator und Templates für eine spezielle Zielplattform
DSL-Editor
Erstellung eines (graphischen) DSL-Editors