Software Factories SS 2018 - Prof. Dr. Dirk Müller - HTW Dresden
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Übersicht
● Motivation: pro und contra Java
● Hello-World-Beispiel
● Sichtweisen, Einordnung und Geschichte
● Beispiel „Liste von Strings in Großbuchstaben“
● wichtige Schlüsselwörter
● Active Annotations
● Operator-Überladung
● Template Expressions
● Codegenerierung für das HelloDSL-Beispiel
● Codegenerierung für einen Prolog-zu-Scheme-Transpiler
● Zusammenfassung
Dirk Müller: Software Factories
SS 2018 2/37Java
● Vorteile
– plattformunabhängig durch Bytecode-Ansatz, Ausführung auf JVM
– objektorientierte und imperative Paradigmen kombiniert
– mächtige Bibliotheken
– sehr gute integrierte Entwicklungsumgebungen (frei) verfügbar
● Nachteile Umwandlung einer Liste von
– Redundanz im Quellcode mit Typangaben Strings in die zugehörige Liste
von Strings in Großbuchstaben
– funktionales Paradigma kaum umgesetzt
– keine Operatorüberladung
public ArrayList toUpperCase(final List strings) {
final ArrayList result = new ArrayList();
for (final String string : strings) {
String _upperCase = string.toUpperCase();
result.add(_upperCase);
}
return result;
}
Dirk Müller: Software Factories
Quelle: [2]
SS 2018 3/37Hello World!
package example1
class HelloWorld {
def static void main(String[] args) {
println('Hello World!')
package example1;
}
import org.eclipse.xtext.xbase.lib.InputOutput;
}
@SuppressWarnings("all")
public class HelloWorld {
public static void main(final String[] args) {
InputOutput.println("Hello World!");
}
}
● Xtend-Code wird beim Speichern automatisch (on-the-
fly) in korrespondierenden Java-Code übersetzt und im
Ordner xtend-gen (anpassbar) abgelegt
● auffällige Einsparungen
– Semikolons als Trenner zwischen Anweisungen
– Rückgabetypen
Dirk Müller: Software Factories
SS 2018 4/37Am 20.03.2018
erschienen!
Java 10 bereits heute
● flexibler und ausdrucksstarker
Java-Dialekt
– keine Interoperabilitätsprobleme mit Java
● wird zu gut lesbarem Java-5-kompatiblen
(anpassbar) Code kompiliert
● nahtlose Nutzung bestehender
Java-Bibliotheken
● Ausführung (fast) so schnell wie bei Handcodierung
● Paradigmen: objektorientiert, imperativ und funktional
● Typ-Inferenz, Makros (Active Annotations), Lambda-
Ausdrücke und Operatorüberladung als einige Highlights
● statische Typisierung
– gut für Fehlersuche und IDE-Integration
Dirk Müller: Software Factories
Quelle: [5]
SS 2018 5/37Android-GUI-Programmierung mit Xtend
● nur von Mini-Laufzeitbibliothek org.eclipse.xtend.lib
abhängig, die hauptsächlich aus Google Guava besteht
– Google Guava sollte sowieso genutzt werden
Quelle: [7] Dirk Müller: Software Factories
SS 2018 6/37Sichtweisen auf Xtend
● abgespecktes Java
● ideale Ergänzung zu einer mit Xtext spezifizierten DSL, um
einen Übersetzer (Transpiler) oder einen Compiler selbst
zu schreiben
● „Xtend ist ein Java-Dialekt, der mir weniger Geschwätz
und mehr Funktionalität bietet“ [4]
● „Java + Xtend == Better Java“ [2]
● ein integrierender Java-Transpiler
● ein typsicherer Ersatz für Java Server Pages (JSP) [6]
Dirk Müller: Software Factories
SS 2018 7/37Einordnung
● JVM-Sprachen nutzen die hochoptimierte Java Virtual
Machine zur Ausführung von Bytecode
org.eclipse.xtend.lib
On-the-fly-Transpiler
● z. B. Scala, Groovy, Clojure, JRuby
Scala-Quellcode Java-Quellcode Xtend-Quellcode
(.scala) (.java) (.xtend)
Groovy-Quellcode
(.groovy) javac
Clojure-Quellcode Vorteile
(.clj,cljs,.cljc,.edn) ● Performanz
Java-Bytecode (.class) ● einfache Integration
JRuby-Quellcode
● einfache Fehlerbehe-
(.rb)
bung via Java-Debugger
JVM JVM JVM
Windows Linux Mac
Quelle: [6]
Dirk Müller: Software Factories
SS 2018 8/37Geschichte
● Ursprünge in openArchitectureWare (2006-2011) JAX Innovation
– Xpand als statisch typisierte Templatesprache für Award 2007:
1. Groovy
M2C-Transformationen 3. oAW
– Xtend als funktionale Sprache, um das Metamodell mit
zusätzlicher Logik erweitern zu können (M2M-Transformationen)
● 2011 Xtend2 (Suffix .xtend) als Ersatz für Xpand (.xpt)
und Xtend (.exp), später Namensvereinfachung zu Xtend
● Version 2.7.0 vom 2.9.2014
– reife Makros via Active Annotations, z. B. @Accessors
● Version 2.8.0 vom 11.3.2015
– Reverse Engineering von Java zu Xtend incl. Warnungen bei
unübersetzbarem Code (FixMe-Tags) unterstützt
● Version 2.9.0 vom 1.12.2015
– Unterstützung von IntelliJ IDEA und Android Studio
● aktuelle Version 2.11.0 vom 01.02.2017
Quellen: [5][6] Dirk Müller: Software Factories
SS 2018 9/37Beispiel Liste von Strings in Großbuchstaben
public ArrayList toUpperCase(final List strings) {
final ArrayList result = new ArrayList();
for (final String string : strings) {
String _upperCase = string.toUpperCase();
result.add(_upperCase);
} var list = new ArrayList();
return result; in Java 10 [9] möglich
}
Java def toUpperCase(List strings) { 10 Auftreten von
val result = new ArrayList() String/string
for (string : strings) {
result += string.toUpperCase sehr ausdrucksstark
}
return result 3 Auftreten von
} String/string
Xtend imperativ
def toUpperCase(List strings) {
strings.map( string | string.toUpperCase )
Xtend funktional }
Xtend funktional def toUpperCase(List strings) {
strings.map[toUpperCase]
mit Lambda-Ausdrücken
}
Quelle: [2]
Dirk Müller: Software Factories
SS 2018 10/37Wichtige Schlüsselwörter
● class zur Deklaration einer Klasse
– Standardsichtbarkeit ist public
– package zu package private übersetzt (Standard in Java)
– mehrere Public-Klassen in einer Datei erlaubt
– Konstruktoren mit new statt des Klassennamens, public als Std.
● Deklaration von Klassenvariablen
– Standardsichtbarkeit ist private
– val zur Deklaration einer Wert-Variable (final)
– var zur Deklaration einer (echten) Variable, Weglassung möglich
– Weglassung des Typs erlaubt, falls Ableitung aus Initialisierung
● def zur Deklaration einer Methode
– Standardsichtbarkeit ist public
– Rückgabetyp kann aus dem Methodenrumpf abgeleitet werden,
außer bei abstrakten und rekursiven Methoden
Quelle: [5] Dirk Müller: Software Factories
SS 2018 11/37Makros via Active Annotations
● schematische wiederkehrender Code (Boilerplate-Code)
– Assistenten (Wizards) in IDEs Klicken
– Sammlung von Code-Generatoren
unübersichtlich
– wie individueller Code auch handgeschrieben
aufwendig
● Active Annotations als ein modernes Mittel zur
Codegenerierung
– einfache Integration in existierende Projekte
– kürzere Durchlaufzeiten z. B. zur Erstellung von Prototypen
● Grundidee: Übersetzung von Xtend-Code in Java-Code
unter Nutzung einer Bibliothek
● können nicht im selben Projekt genutzt werden, nur in
einem vorgeschalteten oder kompiliert in einer JAR-Datei
● Schnittstellen im Teil org.eclipse.xtend.lib.macro
der Laufzeitbibliothek definiert
Quelle: [5] Dirk Müller: Software Factories
SS 2018 12/37Grundlegende Idee hinter MDSD
Code einer Referenz- Anwendungs-
Anwendung Modell
Analyse
individueller
Separierung
individueller generischer Code
Code Code
schematisch schematisch
wiederkehrender Code wiederkeh- Plattform
render Code
Dirk Müller: Software Factories
SS 2018 13/37Vorgefertigte Active Annotations
● org.eclipse.xtend.lib.annotations als Plug-in / JAR
● Entwurfsmuster in Bibliothek abgelegt
– aktive Weiterentwicklung (z. T. Beta-Versionen)
● @Accessors dient dem Anlegen von Gettern und Settern
für den gekapselten Zugriff auf eine Klassenvariable
● @Data dient der Konvertierung einer Klasse in eine
Wertobjekt-Klasse (z. B. Geldbetrag, Datum)
– keine Identität, nicht veränderbar, Erzeugung in gültigen Zustand
– alles final
– Getter-Methoden werden generiert, Setter-Methoden nicht
– Konstruktor mit Parametern für alle nicht-initialisierten Felder
– equals(Object)- / hashCode()-Methoden werden generiert
– toString()-Methode wird generiert
● @Delegate dient der automatischen Implementierung von
Delegierungsmethoden für Schnittstellen
Quelle: [5] Dirk Müller: Software Factories
SS 2018 14/37package example1;
Generierung von import org.eclipse.xtend.lib.annotations.AccessorType;
import org.eclipse.xtend.lib.annotations.Accessors;
Gettern und Settern import org.eclipse.xtext.xbase.lib.Pure;
@Accessors
@SuppressWarnings("all")
public class Person {
private String name;
private String firstName;
@Accessors({ AccessorType.PUBLIC_GETTER,
AccessorType.PROTECTED_SETTER })
private int age;
package example1 @Accessors(AccessorType.NONE)
import private String internalField;
@Pure keine Seiteneffekte
org.eclipse.xtend.lib.annotations.Accessors
public String getName() {
@Accessors class Person { return this.name;
String name }
String firstName public void setName(final String name) {
@Accessors(PUBLIC_GETTER, this.name = name;
PROTECTED_SETTER) int age }
@Accessors(NONE) String internalField @Pure
} public String getFirstName() {
return this.firstName;
}
public void setFirstName(final String firstName) {
this.firstName = firstName;
klassenspezifische }
Angaben werden @Pure
überschrieben public int getAge() {
return this.age;
}
protected void setAge(final int age) {
this.age = age;
}}
Quelle: [5] Dirk Müller: Software Factories
SS 2018 15/37Operator-Überladung
● Ziel: besser lesbarer Code, Idee von C++ übernommen
● näher an der fachlichen Domäne (typisch: Mathematik),
aber Ausdrucksstärke nicht verbessert (syntactic sugar)
package example1
import org.eclipse.xtend.lib.annotations.Data
public Complex(final double re,
@Data u.a. Konstruktor generiert final double im) {
class Complex { super();
val double re Real- und this.re = re;
val double im this.im = im;
Imaginärteil }
def +(Complex other) {
new Complex(re + other.re, im + other.im)
} Addition
def -() {
new Complex(-re, -im) entgegengesetzte
} Zahl (unärer Operator)
def -(Complex other) {
this + -other Subtraktion als Addition
} der entgegengesetzten
} Zahl
Dirk Müller: Software Factories
SS 2018 16/37Test der Operatoren für komplexe Zahlen
class ComplexExample { package example1;
def static void main(String[] args) { import example1.Complex;
val x = new Complex(1, 2) import org.eclipse.xtext.xbase.lib.InputOutput;
val y = new Complex(-3, 5) @SuppressWarnings("all")
val z = new Complex(0, 3) public class ComplexExample {
alle Typen var result = -x + y public static void main(final String[] args)
abgeleitet result += z
println(result.toString())
{
final Complex x = new Complex(1, 2);
} final Complex y = new Complex((-3), 5);
} final Complex z = new Complex(0, 3);
Complex _minus = x.operator_minus();
wegen @Data mit Complex result = _minus.operator_plus(y);
automatisch Complex _result = result;
generiert, hier über
mit verfügbar result = _result.operator_plus(z);
Xbase-I/O-Bibliothek String _string = result.toString();
aber auch als InputOutput.println(_string);
println(result) }
möglich }
Dirk Müller: Software Factories
SS 2018 17/37Template Expressions
● Ziel: gut lesbare String-Verkettung
– wichtig für Compiler/Transpiler
● von Paar dreifacher Anführungszeichen (''') abgegrenzt
● interpolierte Ausdrücke von französischen
Anführungszeichen («Guillemets») umschlossen
– Zeichensatz sollte UTF-8 sein
– weniger Probleme mit Escape-Konflikten
– über Syntax-Vervollständigung angeboten
– explizit (unter Windows) mit CTRL+< bzw. CTRL+>
● können überall stehen (alles ist ein Ausdruck)
● Typ ist CharSequence, wird ggf. in String umgewandelt
Dirk Müller: Software Factories
SS 2018 18/37FOR-Schleifen in Templates
– BEFORE fügt Vorspann-Text ein
– AFTER fügt Abspann-Text ein
– SEPARATOR fügt Text zwischen zwei aufeinanderfolgenden
Iterationen ein (Trenner)
– BEFORE und AFTER nur ausgeführt, wenn min. 1 Iteration
– SEPARATOR nur ausgeführt, wenn min. 2 Iterationen
Dirk Müller: Software Factories
SS 2018 19/37Whitespace-Behandlung in Xtend-Templates
Traffic light colors:
red
yellow
green
println(new HelloWorld().html)
● Formatierung bei Templatesprachen klassisch über
Nachschaltung eines Formatter bzw. Pretty Printer gelöst
● Code des Generators und des gen. Codes übersichtlich
● Xtend-Ansatz: Unterscheidung, welche Einrückungen für
Ausgabe gedacht sind und welche nicht (Kontrollstrukturen
wie z. B. FOR oder IF), sichtbar per Syntax-Hervorhebung
Dirk Müller: Software Factories
SS 2018 20/37Einhaken in den Generator-Mechanismus
Datei anlegen und
ersten Text dort
eintragen
funktional: filtern,
liefert einen Baum- Namen extrahieren,
Iterator mit Kommas trennen
als Sicht auf diese
Ressource
Dirk Müller: Software Factories
SS 2018 21/37Codegenerierung in Java übersetzt
Quelldatei in der
Sprache Xtend
generierte Zieldatei
in Java
Dirk Müller: Software Factories
SS 2018 22/37Test der Codegenerierung
Test in zweiter
Eclipse-Instanz
Speichern als
Trigger für die
Codegenerierung
Dirk Müller: Software Factories
SS 2018 23/37Zielcode generiert
Zieldatei
Dirk Müller: Software Factories
SS 2018 24/37Neuer Eintrag im Quellcode
Dirk Müller: Software Factories
SS 2018 25/37Konsistente Änderung im Zielcode
Dirk Müller: Software Factories
SS 2018 26/37Liste zu grüßender Personen mit Templates
override void doGenerate(Resource resource,
IFileSystemAccess2 fsa, IGeneratorContext context)
{
fsa.generateFile('greetings.txt', 'People to greet: ' +
resource.allContents
.filter(typeof(Greeting))
.map[name]
.join(', ')) ohne Templates
}
override void doGenerate(Resource resource,
mit Templates
IFileSystemAccess2 fsa, IGeneratorContext context)
{
fsa.generateFile('greetings.txt', 'People to greet: ' + '''
«FOR greeting : resource.allContents
.filter(Greeting)
.toIterable
SEPARATOR ', '»«greeting.name»«ENDFOR»
''')
} wichtig, dass in 1 Zeile, sonst Umbrüche in der Liste
Dirk Müller: Software Factories
SS 2018 27/37Einstellungen für den Xtend-Transpiler
Java-Version für Zielcode
Warnungen unterdrückt
Zielverzeichnis
Nachfrage beim Versuch,
generierte Dateien zu editieren
Dirk Müller: Software Factories
SS 2018 28/37Beispiel: Lied „99 Bottles of Beer“
99 Bottles of Beer
99 bottles of beer on the wall, 99 bottles of beer.
Take one down and pass it around, 98 bottles of beer on the wall.
98 bottles of beer on the wall, 98 bottles of beer.
Take one down and pass it around, 97 bottles of beer on the wall.
[..]
2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.
„One“ als Variante „one“ „one“ als Variante
1 bottle of beer on the wall, 1 bottle of beer.
Take one down and pass it around, no more bottles of beer on the wall.
No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.
Quelle: [8] Dirk Müller: Software Factories
SS 2018 29/37Bottles-Song in BASIC
10 REM BASIC Version of 99 Bottles of beer
20 FOR X=100 TO 1 STEP -1
30 PRINT X;"Bottle(s) of beer on the wall,";X;"bottle(s) of beer"
40 PRINT "Take one down and pass it around," Singular/Plural unsauber
50 PRINT X-1;"bottle(s) of beer on the wall"
60 NEXT Endvers nicht abgebildet
10 REM Basic version of 99 bottles of beer
20 REM Modified by M. Eric Carr (eric@carrnet.net)
30 REM from prior version found on this site.
40 REM (Modified to correct "1 bottle" grammar)
50 FOR X=99 TO 1 STEP -1
60 PRINT X;"bottle"; GOSUB-Unterprogramm
70 IF X1 THEN PRINT "s"; nicht genutzt
80 PRINT " of beer on the wall,";X;"bottle";
90 IF X1 THEN PRINT "s";
100 PRINT " of beer"
110 PRINT "Take one down and pass it around,"
120 PRINT X-1;"bottle";
130 IF X1 THEN PRINT "s";
140 PRINT " of beer on the wall"
150 NEXT Endvers nicht abgebildet
Quelle: [8] Dirk Müller: Software Factories
SS 2018 30/37Bottles-Song in Java
class Bottle {
public static void main(String args[]) {
singTheSong(99);
}
private static void singTheSong(int all) {
for (int i=all; i>0; i--) {
System.out.println(Bottles(i) + " of beer on the wall, " + bottles(i)
+ " of beer.");
System.out.println("Take one down and pass it around, " + bottles(i-1)
+ " of beer on the wall.");
System.out.println();
}
println("No more bottles of beer on the wall, no more bottles of beer.");
System.out.println("Go to the store and buy some more, " + bottles(all)
+ "of beer on the wall.");
}
private static String bottles(int i) {
switch(i) {
case 0: return "no more bottles";
case 1: return "one bottle";
default: return i + " bottles";
}
}
private static String Bottles(int i) {
return bottles(i).substring(0,1).toUpperCase() + bottles(i).substring(1);
}
} Dirk Müller: Software Factories
SS 2018 31/37Bottles-Song in Xtend
class BottlesSong {
def static void main(String[] args) {
println(new BottlesSong().singTheSong(99))
} Template Expression
def singTheSong(int all) '''
«FOR i : all .. 1»
«i.Bottles» of beer on the wall, «i.bottles» of beer.
Take one down and pass it around, «(i - 1).bottles» of beer on the wall.
«ENDFOR»
No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, «all.bottles» of beer on the wall.
'''
def static bottles(int i) { Powerful Switch Expression
switch i {
1 als case 0 : 'no more bottles'
case 1 : 'one bottle' Template Expression
Zahlwort
default : '''«i» bottles'''
}.toString Extension method
}
def static Bottles(int i) {
bottles(i).toFirstUpper Everything is an expression.
}
}
Dirk Müller: Software Factories
SS 2018 32/37Codegenerierung für einen
Prolog-zu-Scheme-Transpiler
/*
* generated by Xtext 2.9.2
*/
package de.htwdd.sf.beleg.muellerd.generator Xtext generiert eine Schablone
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.AbstractGenerator
import org.eclipse.xtext.generator.IFileSystemAccess2
import org.eclipse.xtext.generator.IGeneratorContext
/**
* Generates code from your model files on save.
*
* See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation
*/
class PrologDslGenerator extends AbstractGenerator { Callback-Routine, die beim Speichern
des Quelltextes aufgerufen wird
override void doGenerate(
Resource resource, AST vom Parsen mittels Xtext geliefert
IFileSystemAccess2 fsa,
IGeneratorContext context)
{ Zugriff aufs Dateisystem
} muss noch gefüllt werden
}
Dirk Müller: Software Factories
SS 2018 33/37Implementierung von doGenerate
class PrologDslGenerator extends AbstractGenerator {
String code // zur Konstruktion des auszugebenden Textes
override void doGenerate(Resource resource,
IFileSystemAccess2 fsa, Filtern des passenden
IGeneratorContext context) Typs und iterierbar
{ notwendige Initialisierung zum leeren String machen
code = ""
for (e : resource.allContents.filter(PrologDsl).toIterable)
{
System.out.println("Start Transformieren") Kontrollausgabe
e.traversiere Methode, die noch für alle in der Konsole der
} Typen implementiert wird ersten Eclipse-Instanz
fsa.generateFile("PrologDsl.lsp", code)
Herausschreiben des
}
Endergebnisses
def conc(String str) { // zum Anfügen neuer Programmteile
code = code + str;
}
Hilfsmethode
}
Dirk Müller: Software Factories
SS 2018 34/37Implementierung einer Regel in Xtend
→ ( prolog (quote ) (quote ))
def traversiere(PrologDsl k) {
conc("(prolog (quote") noch zu implementieren
k.program.traversiere
conc(")\r\n(quote") Zeilenumbruch zur besseren Lesbarkeit
k.query.traversiere noch zu implementieren
conc("))")
}
Besser lesbare Lösung mittels Templates möglich?
Dirk Müller: Software Factories
SS 2018 35/37Zusammenfassung
● Xtend als statisch typisierte, auf Java aufbauende Sprache
– Typen können oft abgeleitet werden
– funktionales Paradigma neben dem objektorientierten und
imperativen unterstützt
– alles ist ein Ausdruck => flexible Komponierbarkeit
– sehr kompakter und ausdrucksstarker Code
● gute Eignung zur Code-Generierung
– Implementierung der Methode doGenerate
● gute Template-Unterstützung
– flexible Kontrollstrukturen
– besonders gut gelungen: Behandlung von Whitespaces mit guter
Lesbarkeit im Code-Generator und im generierten Code
● Makros für schematisch wiederkehrenden Code via Active
Annotations und Operatorüberladung als Highlights
Dirk Müller: Software Factories
SS 2018 36/37Literatur
[1] Hartmut Fritzsche, „Software Factories – Skript zur Lehrveranstaltung“, 11.01.2016,
Download am 6.4.2016,
http://www2.htw-dresden.de/~fritzsch/SF/Software_Factories_Skript.pdf
[2] Sven Efftinge, Sebastian Zarnekow: „Extending Java“, in: Pragmatic Programmer
Magazine, Dezember 2011, Download am 30.04.2016,
https://pragprog.com/magazines/2011-12/extending-java
[3] Alex Blewitt: „Xtend Extends Java“, in: InfoQ, Juni 2012; (Interview mit Sven Efftinge),
Download am 30.04.2016, http://www.infoq.com/news/2012/06/xtend-release-10
[4] Jan Koehnlein auf Twitter, 21.04.2016, Download am 30.04.2016,
https://twitter.com/JAXenter/status/723098924114833408
[5] Xtend-Dokumentation, https://eclipse.org/xtend/index.html
[6] Meinte Boersma: „Meinte's DSL-Blog: Using Xtext’s Xtend(2) language“, 19.09.2011,
Download am 3.5.2016,
https://dslmeinte.wordpress.com/2011/09/19/using-xtexts-xtend2-language/
[7] Sven Efftinge: „sven eftinge's blog: Writing Android UIs with Xtend“, 12.12.2011,
Download am 6.5.2016,
http://blog.efftinge.de/2011/12/writing-android-uis-with-xtend.html
[8] Oliver Schade: „99 Bottles of Beer“, 2016, Download am 01.03.2018
http://99-bottles-of-beer.net
[9] Oracle: „JEP 286: Local-Variable Type Inference“, 6.3.2018, Download am 21.3.2018,
http://openjdk.java.net/jeps/286
Dirk Müller: Software Factories
SS 2018 37/37Sie können auch lesen