Software Factories - SS 2020 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 2020 2/37
Java ● 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 2020 3/37
Hello 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 2020 4/37
Am 20.03.2018 erschienen! 03/2020 sogar Java 10 bereits heute schon Java 14 ● 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 2020 5/37
Android-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 2020 6/37
Sichtweisen 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 2020 7/37
Einordnung ● 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 2020 8/37
Geschichte ● 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.21 vom 3.3.2020 Quellen: [5][6] Dirk Müller: Software Factories SS 2020 9/37
Beispiel 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 2020 10/37
Wichtige 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 2020 11/37
Makros 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 2020 12/37
Grundlegende 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 2020 13/37
Vorgefertigte 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 2020 14/37
package 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 2020 15/37
Operator-Ü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 2020 16/37
Test 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 2020 17/37
Template 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 2020 18/37
FOR-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 2020 19/37
Whitespace-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 2020 20/37
Einhaken 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 2020 21/37
Codegenerierung in Java übersetzt Quelldatei in der Sprache Xtend generierte Zieldatei in Java Dirk Müller: Software Factories SS 2020 22/37
Test der Codegenerierung Test in zweiter Eclipse-Instanz Speichern als Trigger für die Codegenerierung Dirk Müller: Software Factories SS 2020 23/37
Zielcode generiert Zieldatei Dirk Müller: Software Factories SS 2020 24/37
Neuer Eintrag im Quellcode Dirk Müller: Software Factories SS 2020 25/37
Konsistente Änderung im Zielcode Dirk Müller: Software Factories SS 2020 26/37
Liste 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 2020 27/37
Einstellungen 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 2020 28/37
Beispiel: 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 2020 29/37
Bottles-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 2020 30/37
Bottles-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 2020 31/37
Bottles-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 2020 32/37
Codegenerierung 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 2020 33/37
Implementierung 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 2020 34/37
Implementierung 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 2020 35/37
Zusammenfassung ● 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 2020 36/37
Literatur [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 2020 37/37
Sie können auch lesen