Messung des Einflusses von Typsystemen auf die Entwicklungszeit von kleinen Softwareprojekten anhand von Java und Groovy


Bachelorarbeit, 2010

85 Seiten, Note: 1,3


Leseprobe

Inhaltsverzeichnis

Zusammenfassung

Abstract

Abbildungsverzeichnis

Tabellenverzeichnis

1 Motivation

2 Typsysteme
2.1 Einführung in das Typsystem
2.2 Der Lambda-Kalkül nach [30]
2.2.1 Der untypisierte Lambda-Kalkül nach [30]
2.2.1.1 Syntax
2.2.1.2 Evaluationsregeln
2.2.1.3 Beispiel des untypisierten Lambda-Kalküls
2.2.2 Der typisierte Lambda-Kalkül nach [30]
2.2.2.1 Syntax
2.2.2.2 Typregeln
2.2.2.3 Beispiel des typisierten Lambda-Kalküls
2.2.3 Beispiel für die Anwendung des typisierten und untypisierten Lambda-Kalküls
2.3 Argumente für oder gegen Typsysteme
2.4 Codebeispiel für die Vor- und Nachteile von Typsystemen
2.5 Typisierte und untypisierte Programmiersprachen
2.5.1 Python und Pylint
2.5.2 Ruby und DiamondbackRuby (DRuby)
2.5.3 Purity
2.5.4 Java und Groovy
2.6 Zusammenfassung

3 Empirische Softwareforschung
3.1 Einführung
3.2 Kontrollierte Experimente
3.3 Evaluation von Software
3.4 Messung in empirischen Studien
3.5 Verwendung von Studenten als Probanden
3.5.1 Vor- und Nachteile von Studenten
3.5.2 Klassifizierung von Probanden
3.5.3 Ethische Fragen für die Verwendung von Studenten als Probanden
3.6 Zusammenfassung

4 Verwandte Arbeiten
4.1 Empirische Studien zu Typsystemen
4.2 Empirische Studien zu Programmiersprachkonstrukten
4.3 Zusammenfassung

5 Experiment
5.1 Geplanter Experimentaufbau
5.2 Verwendete Programmiersprachen
5.3 Entwicklungsumgebung und Hardware
5.4 Testaufgaben
5.5 Aufgabenpaket
5.5.1 Aufgabenpaket 1
5.5.1.1 Aufgabe2für Java
5.5.1.2 Aufgabe3für Java
5.5.1.3 Aufgabe4für Java
5.5.1.4 Aufgabe5für Java
5.5.1.5 Aufgabe6für Java
5.5.1.6 Aufgabenpaket 2
5.6 Tatsächlicher Experimentablauf
5.7 Gültigkeit des Experiments_

6 AuswertungdesExperiments
6.1 VorbereitungderDaten fürdie Auswertung
6.2 Auswertung der Daten
6.2.1 Verwendete statistische Verfahren
6.2.2 Gemessene Daten und deskriptive Statistik
6.2.3 Ist eine gemeinsame Verwendung der Daten möglich?
6.2.4 Gibt es einen Unterschied zwischen Java und Groovy?
6.2.5 Wie groß ist der Unterschied zwischen den Entwicklungszeiten?

7 Diskussion
7.1 Generelle Diskussion

8 Zusammenfassung und Ausblick

9 Literatur

10 Anhang
10.1 Fragebogen und Messung der Tippgeschwindigkeit zu Beginn des Experiments 61
10.2 Testaufgabe für Java
10.3 Testaufgabe für Groovy
10.4 Javaaufgaben
10.4.1 Aufgabe 1: Einfügen von Spielern
10.4.2 Aufgabe 2: doAbstauberTor
10.4.3 Aufgabe 3: doTorschuss
10.4.4 Aufgabe 4: doGrätsche
10.4.5 Aufgabe 5: spielerTraumpass
10.4.6 Aufgabe 6: auswechslung
10.4.7 Aufgabe 7: changePosition
10.5 Groovyaufgaben
10.5.1 Aufgabe 1: Einfügen eines Teilnehmers
10.5.2 Aufgabe 2: doSchlag
10.5.3 Aufgabe 3: doAntwort
10.5.4 Aufgabe 4: doBeleidigung
10.5.5 Aufgabe 5: doHausaufgaben
10.5.6 Aufgabe 6: changeType
10.5.7 Aufgabe 7: doBewertung
10.6 Fragebogen nach dem Experiment

Zusammenfassung

In dieser Arbeit wird ein kontrolliertes Experiment vorgestellt, welches im Rahmen dieser Bachelorarbeit durchgeführt und ausgewertet wurde. Dabei wurde empirisch gemessen, ob ein Typsystem Einfluss auf die Entwicklungszeit von kleinen Softwareprojekten hat: 21 Probanden lösten mithilfe der Programmiersprachen Java und Groovy Aufgaben, die speziell für dieses Experiment konzipiert wurden. Anschließend wurden ihre Entwicklungszeiten miteinander verglichen.

Abstract

This paper presents a controlled experiment which has been carried out and ana-lyzed in the context of this bachelor thesis. It deals with the question if a type system influences the development time of small software programs. With the help of the programming languages Java and Groovy, 21 subjects solved tasks that were espe-cially developed for this experiment. Following, their development times have been compared.

Abbildungsverzeichnis

Abbildung 1: Syntax des untypisierten Lambda-Kalküls nach [30]

Abbildung 2: ß-Abstraktion nach [30]

Abbildung 3: Anwendung des untypisierten Lambda-Kalküls anhand eines Beispiels

Abbildung 4: Syntax des typisierten Lambda-Kalküls nach [30]

Abbildung 5: Syntax der Typen in einem Typsystem nach [30]

Abbildung 6: Typregeln in Anlehnung an [30]

Abbildung 7: Typregel T-VAR nach [30]

Abbildung 8: Ausgangssituation des Beispiels

Abbildung 9: Ausgangssituation und Ergebnis_

Abbildung 10: Anwendung der Typregel T-APP

Abbildung 11: Beispielcode für die Vor- und Nachteile von Typsystemen anhand von Java 11

Abbildung 12: Beispielcode in der Programmiersprache Python

Abbildung 13: Beispielcode in Ruby mit der Erweiterung DRuby

Abbildung 14: Beispielcode für typisiertes und untypisiertes Purity aus [47]

Abbildung 15: Objektmodell für die Javaaufgaben

Abbildung 16: Musterlösung der Teilaufgabe 2c) für Java

Abbildung 17: Musterlösung der Teilaufgabe 3b) für Java

Abbildung 18: Musterlösung der Teilaufgabe 4c) für Java

Abbildung 19: Musterlösung der Teilaufgabe 5c) für Java

Abbildung 20: Musterlösung der Teilaufgabe 6c) für Java

Abbildung 21: Objektmodell für die Groovyaufgaben

Abbildung 22: Histogramme mit Normalverteilungskurve für eine kleine Auswahl von Daten

Tabellenverzeichnis

Tabelle 1: Aufteilung der Aufgaben und Compiler an die vier Gruppen aus [34]

Tabelle 2: Aufbau des Experiments aus [32]

Tabelle 3: Entwicklungszeiten aller Probanden

Tabelle 4: Auswertung eines Teils des Fragebogens

Tabelle 5: Deskriptive Daten für die Entwicklungszeiten aller Probanden

Tabelle 6: Deskriptive Daten für bestimmte Entwicklungszeiten

Tabelle 7: Ergebnis des Kolmogorov-Smirnov-Tests für die Frage nach der gemeinsamen Verwendung der Daten

Tabelle 8: Ergebnis des Wilcoxon-Tests für die Gesamtzeit und für die Entwicklungszeit aller Aufgaben einzeln

Tabelle 9: Ergebnis des Wilcoxon-Tests für die Entwicklungszeit der besten Probanden bei den Testaufgaben unterteilt nach Java und Groovy

Tabelle 10: Ergebnis des Wilcoxon-Tests für die Entwicklungszeiten der schlechtesten Probanden bei der Lösung der Testaufgaben

Tabelle 11: Ergebnis des Wilcoxon-Tests für die Entwicklungszeiten der zwölf besten Probanden nach Auswertung der Fragebögen

Tabelle 12: Ergebnis des Wilcoxon-Tests für die Entwicklungszeit der Probanden mit der geringsten Programmiererfahrung

Tabelle 13: Ergebnis des Kolmogorov-Smirnov-Tests und des T-Tests für die Entwicklungszeiten aller Probanden_

Tabelle 14: Ergebnis des Kolmogorov-Smirnov-Tests und des T-Tests für die Entwicklungszeiten der elf schnellsten Probanden bei der Lösung beider Testaufgaben

Tabelle 15: Ergebnis des Kolmogorov-Smirnov-Tests und des T-Tests für die Entwicklungszeiten der zehn langsamsten Probanden bei der Lösung beider Testaufgaben

Tabelle 16: Ergebnis des Kolmogorov-Smirnov-Tests und des T-Tests für die Entwicklungszeiten der zwölf besten Probanden nach Selbsteinschätzung im Fragebogen

Tabelle 17: Ergebnis des Kolmogorov-Smirnov-Tests und des T-Tests für die Entwicklungszeiten der neun schlechtesten Probanden nach Selbsteinschätzung im Fragebogen

1 Motivation

Typsysteme haben seit den späten 1950er Jahren zunehmend an Bedeutung gewonnen [30]. Sie wurden in Fortran zur Unterscheidung zwischen den Typen Integer und Float eingesetzt. In den 1970er Jahren wurden immer mehr Konzepte in Form von Polymorphismus, abstrakten Datentypen und Modulsystemen eingeführt. Zur gleichen Zeit begannen Computerwissenschaftler die Typsysteme von Programmiersprachen mit denen aus der mathematischen Logik zu verknüpfen [30]. Dennoch gibt es nur wenige empirische Studien, die sich mit dem Thema Typsysteme beschäftigen.

In dieser Bachelorarbeit wird untersucht, inwieweit Typsysteme sich auf die Programmierung von kleinen Programmen auswirken. In diesem Zusammenhang wird ein kontrolliertes Experiment vorgestellt, in dem die Entwicklungszeiten einer statisch typisierten Programmiersprache mit denen einer dynamisch typisierten Programmiersprache verglichen werden.

Eine kurze Einführung in Typsysteme wird in Kapitel zwei gegeben. Desweiteren werden mehrere typisierte und untypisierte Programmiersprachen kurz vorgestellt. Kapitel drei gewährt einen Einblick in] die empirische Softwareforschung, wo kontrollierte Experimente sowie Mess- und Evaluationsverfahren vorgestellt werden. Einen Überblick über verwandte empirische Arbeiten, die sich mit dem Thema Typsysteme und anderen Programmiersprachkonstrukten beschäftigen, gibt Kapitel vier. In Kapitel fünf wird das Experiment, welches speziell für diese Bachelorarbeit entwickelt wurde, und der Experimentablauf vorgestellt. Die Auswertung von gesammelten Daten des kontrollierten Experiments mit verschiedenen statistischen Verfahren wird in Kapitel sechs präsentiert. Kapitel sieben geht auf die Gültigkeit und die Erkenntnisse dieses Experiments ein und im letzten Kapitel werden eine kurze Zusammenfassung und ein Ausblick auf weitere mögliche Arbeiten gegeben.

2 Typsysteme

In der Softwareentwicklung werden heutzutage viele verschiedene formale Methoden benötigt, um die Korrektheit des Systems zu garantieren [30]. Dazu gehören zum Beispiel die Laufzeitüberwachung oder Typsysteme. In diesem Kapitel wird ein kurzer Einblick in das Thema Typsysteme gegeben. Hierbei wird der Lambda-Kalkül vorgestellt und deren Nutzen an einem kurzen Beispiel aufgezeigt. Desweiteren werden die Vor-und Nachteile von Typsystemen anhand von Meinungen verschiedener Autoren dargestellt. In Kapitel 2.5 werden abschließend einige typisierte und untypisierte Programmiersprachen vorgestellt.

2.1 Einführung in das Typsystem

Unter einem Typsystem wird nach [22] derjenige Bestandteil eines Compilers oder einer Laufzeitumgebung bezeichnet, der ein Programm auf die korrekte Verwendung der Datentypen überprüft. Auf diese Weise soll das Auftreten von Fehlern während der Ausführung eines Programms verhindert werden [11]. Neben der Überprüfung der Datentypen bezeichnet ein Typsystem sowohl diejenigen Typen, die für das Programm genutzt werden können, als auch die Beziehungen zwischen dem Programm und den Typen [11]. Typprüfer sind typischerweise im Compiler eingebaut [30]. Dies setzt voraus, dass sie ihre Aufgabe automatisch durchführen können.

Die Überprüfung der Typen auf ihre Korrektheit erfolgt auf zwei Arten. Bei der statischen Prüfung wird die Typprüfung bereits zur Übersetzungszeit durch den Compiler durchgeführt und alle vorhandenen Inkonsistenzen werden in Form von Fehlermeldungen oder Warnungen an den Benutzer zurückgemeldet [22]. Die dynamische Typprüfung liegt dann vor, wenn Variablen ohne Typ deklariert werden und daher Objekte beliebigen Typs enthalten können [29]. Im zweiten Fall wird die Prüfung erst während der Programmausführung durch den Interpreter oder die Laufzeitumgebung durchgeführt [22].

Statisch typisierte Sprachen verlangen die Deklaration von Typen und benachrichtigen den Programmierer beim Kompilieren über typrelevante Fehler, die korrigiert werden müssen [44]. Dabei kann einer Variablen nur ein Objekt zugewiesen werden, das mit dem statisch festgelegten Datentyp kompatibel ist [22]. Ein Beispiel dafür ist aus der statisch typisierten Programmiersprache Java1 das Statement int i = 1. Die Variable i kann nur den Wert einer Ganzzahl annehmen. Eine Zeichenkette, zum Beispiel „test", würde beim Kompilieren zu einem Typfehler führen. Im Gegensatz dazu gibt es die dynamisch typisierten Programmiersprachen, bei denen die verwendeten Variablen nicht an Objekte eines bestimmten Datentyps gebunden sind [22]. In dem Statement i = 1 aus der dynamisch typisierten Programmiersprache Python2 könnte die Variable i auch im späteren Verlauf eine Zeichenkette in Form von „test" annehmen.

In dem folgenden Kapitel wird der Lambda-Kalkül formal vorgestellt, um die Arbeitsweise und den Nutzen eines Typsystems aufzuzeigen. Es wird desweiteren anhand eines kurzen Beispiels die Anwendung des Lambda-Kalküls aufgezeigt.

2.2 Der Lambda-Kalkül nach [30]

Der Lambda-Kalkül ist ein formales System, das von Alonso Church in den 30er Jahren des 20. Jahrhunderts entwickelt wurde [3]. Es reduziert alle Funktionsdefinitionen und Anwendungen auf Basisoperationen [30]. Für die Spezifikation von Prog-rammierspracheigenschaften, dem Sprachdesign und der Implementierung und das Erforschen von Typsystemenbildet der Lambda-Kalkül die Grundlage [30]. Er diente auch als Basis für die Definition des Pi-Kalküls von Milner, Parrow und Walker [siehe [28] für eine detaillierte Beschreibung] und des Objekt-Kalküls von Abadi und Cardelli [siehe [1] für weitere Informationen].

2.2.1 Der untypisierte Lambda-Kalkül nach [30]

„Im Lambda-Kalkül ist alles eine Funktion" [30]. Die Argumente, die von einer Funktion akzeptiert werden, sind ebenfalls Funktionen sowie das Ergebnis einer Funktion wiederum eine Funktion ist. Der Lambda-Kalkül stellt jede Funktionsdefinition und Applikation in der einfachsten Form dar.

2.2.1.1 Syntax

Der Lambda-Kalkül besteht aus drei Arten von Termen. Dazu zählen die Variable, die Abstraktion und die Applikation. Eine Variable x ist ein Term. Die Abstraktion (Xx.ü) einer Variablen x von einem Term t1 ist ebenfalls ein Term. Auch die Applikation (t t) eines Terms t1 zu einem anderen Term t2 ist ein Term. In der nachfolgenden Abbildung werden die Möglichkeiten der Termebildung nach [30] vorgestellt.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 1: Syntax des untypisierten Lambda-Kalküls nach [30]

2.2.1.2 Evaluationsregeln

Der Lambda-Kalkül besteht in seiner Grundform aus keinen Einschränkungen oder primitiven Operatoren. Es hat keine arithmetischen Operationen, Bedingungen, Schleifen oder Sequenzen. Die einzige Berechnung, die im puren Lambda-Kalkül vorhanden ist, ist die Umwandlung der Applikationen von Funktionen in Argumente, die wiederum Funktionen sind. Dabei wird bei jedem Schritt in einer Applikation die rechte Komponente in die Variable der ersten Abstraktion auf der linken Seite eingesetzt. Abbildung 2 verdeutlicht diese Regel, die als ß-Abstraktion bezeichnet wird.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 2: ß-Abstraktion nach [30]

Die ß-Abstraktion in der Abbildung 2 sagt aus, dass x im Term t12 durch den Term t2 ersetzt wird.

2.2.1.3 Beispiel des untypisierten Lambda-Kalküls

In diesem Kapitel wird die Anwendung des untypisierten Lambda-Kalküls an einem kurzen Beispiel (siehe Abbildung 3) dargestellt. Die Ausgangssituation beinhaltet eine Applikation, die wiederum aus einer Applikation und zwei Abstraktionen besteht.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 3: Anwendung des untypisierten Lambda-Kalküls anhand eines Beispiels

Im ersten Schritt wird die ß-Abstraktion auf die Applikation (Äx. Ay. yx)a angewendet. Dabei wird die Variable a in die Funktion Äx eingesetzt. Anschließend erfolgt die erneute Anwendung der ß-Abstraktion auf diese Applikation. Dabei wird der Term Az.z in die Abstraktion für die Variable y eingesetzt (Schritt 2). Als Letztes wird erneut die ß-Abstraktion auf die Applikation durchgeführt. Es wird die Variable a in die Abstraktion Az.z für die Variable z eingesetzt. Die Applikation (Ax.Äy.y x) a (Az.z) evaluiert durch mehrmalige Anwendung der ß-Abstraktion zu der Variable a.

2.2.2 Der typisierte Lambda-Kalkül nach [30]

Der typisierte Lambda-Kalkül wurde von Church und Curry entwickelt. Die Idee ist, dass ein Typsystem mit dem puren Lambda-Kalkül kombiniert wird [30]. Aus diesem Grund werden Typregeln für die Variablen, Abstraktionen und Applikationen definiert, die Typsicherheit garantieren und Typen beinhalten, die für heutige Programmiersprachen relevant sind.

2.2.2.1 Syntax

Abbildung 4 zeigt die Syntax der Terme im typisierten Lambda-Kalkül, welcher wie der untypisierte Lambda-Kalkül aus drei Termen besteht. Hierzu gehören die Variable, die Abstraktion und die Applikation. Der einzige Unterschied besteht bei der Abstraktion. Dort wird zusätzlich bei der Funktion angegeben, von welchem Typ die gebundene Variable ist.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 4: Syntax des typisierten Lambda-Kalküls nach [30]

Hinzu kommt im typisierten Lambda-Kalkül noch die Beschreibung von Typen. Dabei wird angegeben, welche Typen in einem Typsystem existieren. Abbildung 5 sagt aus, dass es die Typen Boolean und Number im Typsystem gibt.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 5: Syntax der Typen in einem Typsystem nach [30]

2.2.2.2 Typregeln

Die folgenden Typregeln definieren die konkreten Ausprägungen von Typen, die ein Term annehmen kann. In Abbildung 6 sind die Typregeln für die Datentypen Boolean und Number dargestellt.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 6: Typregeln in Anlehnung an [30]

Die Typregel T-TRUE besagt, dass der Term true vom Typen Bool ist. Analog dazu sagt die Regel T-FALSE aus, dass der Term false ebenfalls vom Typ Bool ist. Die beiden Regeln TO und TI definieren, dass der Term 0 bzw. der Term 1 vom Typen Number sind. Die letzte Regel TNot sagt aus, dass der Term Not vom Typ Bool nach Bool ist. Es wird ein boolscher Wert eingegeben und es gibt einen boolschen Wert als Ausgabe.

Neben den Typregeln für die Typen gibt es noch jeweils eine Regel für die Variablen, Abstraktionen und Applikationen, die in der nachfolgenden Abbildung dargestellt sind.

Die Konklusion (oberer Teil) der Regel T-VAR ist, dass aus der Umgebung r geschlossen wird, dass die Variable x vom Typen T ist. Dies gilt aber nur unter der Prämisse (unterer Teil der Typregel T-VAR), dass x vom Typen T ist und der Typ T ein Element der Umgebung r ist.

Die Konklusion (oberer Teil) der Typregel T-ABS ist, dass aus der Umgebung T geschlossen wird, dass der Typ T1 der Funktion Ax zum Typen von t2 resultiert. Dies geschieht aber nur unter der Prämisse (unterer Teil der Typregel T-ABS), dass aus der Umgebung T geschlossen wird, wenn x zum Typen T1 evaluiert, dass t2 zum Typen T2 evaluiert. Das Komma bindet den Ausdruck in eckigen Klammern an die Umgebung T.

In der Typregel T-APP für die Applikation ist die Konklusion (oberer Teil), dass die Applikation t1 t2 vom Typ T ist, wenn die Prämisse (unterer Teil) erfüllt ist. Die Prämisse gibt an, dass aus der Umgebung T geschlossen wird, dass t1 zu einer Funktion evaluiert, aus der der Typ Tx zum Typen T resultiert. Weiterhin muss in der Prämisse gelten, dass t2 zum Typen Tx evaluiert.

2.2.2.3 Beispiel des typisierten Lambda-Kalküls

In diesem Kapitel werden die Typregeln des typisierten Lambda-Kalküls an einem Beispiel angewendet. Auf diesem Weg wird überprüft, ob das Beispiel ein gültiger Ausdruck für ein Typsystem ist. Abbildung 8 zeigt die Anwendung der Typregeln an einem Beispiel.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 8: Ausgangssituation des Beispiels

Die Abstraktion in den Klammern in dem Ausgangsterm in Abbildung 8 ist vom Typ Bool und evaluiert zum Typ Bool. Auch der Ausdruck true evaluiert zu Bool.

Im ersten Schritt wird die Typregel T-APP auf den Ausgangsterm angewendet. Das Ergebnis des ersten Schritts zeigt im oberen Teil als Prämisse die Ausgangssituation an. Die Konklusion im unteren Teil ist nur unter dieser Prämisse gültig. Die Prämisse sagt aus, dass aus einer leeren Umgebung geschlossen wird, dass der Term in den Klammern zum Typen Tx evaluiert. Dieser Typ Tx resultiert zum Typen T. Desweiteren wird aus der leeren Umgebung geschlossen, dass true zum Typen Tx resultiert. Auf den rechten Term im oberen Teil wird nun die Typregel T-TRUE angewendet, dessen Ergebnis im zweiten Schritt gezeigt ist.

Der Ausdruck im oberen Teil ist nur gültig unter der Prämisse, dass true vom Typ Bool ist. Daraus lässt sich schließen, dass der Typ Tx ebenfalls vom Typ Bool ist. Dies wird anschließend in den Ausdruck aus dem ersten Schritt eingesetzt. Das Ergebnis ist im dritten Schritt dargestellt.

Im nächsten Schritt wird die Typregel T-ABS auf den Ausdruck im vierten Schritt angewendet. Die Konklusion (unterer Teil) besagt, dass aus der Umgebung T' geschlossen wird, wenn x vom Typ Bool ist, dass Not x vom Typen T ist. Aus dem Term Not x kann abgeleitet werden, dass Not vom Typen Ty nach T ist.

Nun wird aus der Umgebung im fünften Schritt durch Anwendung der Typregel T-VAR geschlossen, dass x vom Typen Ty ist. Dies ist aber nur unter der Prämisse (unterer Teil) gültig, wenn x vom Typen Bool ist. Basierend auf dieser Prämisse lässt sich erschließen, dass Ty vom Typ Bool ist.

Im nächsten Schritt wird für Ty der Typ Bool eingesetzt. Auf den neuen Ausdruck wird nun die Typregel T-NOT angewendet. Das Ergebnis dieser Anwendung ergibt, dass Not vom Typen Bool nach Bool ist. Hieraus lässt sich nun schließen, dass der Typ T vom Typen Bool ist. Somit ist der Ausgangsterm ein gültiger Term.

2.2.3 Beispiel für die Anwendung des typisierten und untypisierten Lambda-Kalküls

In diesem Kapitel werden die Schwächen des untypisierten Lambda-Kalküls anhand eines kurzen Beispiels dargestellt. Es wird gezeigt, dass im untypisierten LambdaKalkül ein Ausdruck gültig ist, der sich aber im typisierten Lambda-Kalkül als ungültig herausstellt. Das Typsystem kann mit diesem Ausdruck nicht arbeiten, obwohl er im untypisierten Lambda-Kalkül ein gültiger Ausdruck ist. In der folgenden Abbildung sind der Ausdruck und das Ergebnis der Anwendung des untypisierten Lambda-Kalküls dargestellt.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 9: Ausgangssituation und Ergebnis

Durch zweifache Anwendung der ß-Abstraktion auf den oberen Term in Abbildung 9 kommt als Ergebnis heraus, dass der Ausdruck zu AND 1 evaluiert. Im nächsten Schritt wird der Ausdruck aus der Ausgangssituation mit dem typisierten LambdaKalkül überprüft, was in der Abbildung 10 dargestellt ist. Im ersten Schritt wurde die Typregel T-APP angewendet.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 10: Anwendung der Typregel T-APP

Nun wird auf den rechten Ausdruck im oberen Teil des ersten Schritts die Typregel T1 angewendet. Anschließend wird für Tx der Typ Num in den Term aus Schritt eins eingesetzt und darauf dann die Typregel T-ABS angewendet.

Im vierten Schritt wird die Typregel T-ABS erneut angewendet. Dies ergibt, dass der Term in den Klammern im oberen Teil vom Typ Num nach T" ist. Dies ist aber nur unter der Prämisse (unterer Teil in Schritt vier) gültig, wenn der Term in der runden Klammer vom Typ T'' ist.

Im nächsten Schritt wird auf den Ausdruck im unteren Teil des vierten Schritts die Typregel T-APP angewendet. Dies evaluiert zu dem Ausdruck im fünften Schritt. Der rechte Ausdruck resultiert nun durch die Anwendung der Typregel T-VAR dazu, dass T''' vom Typ Num ist. Dies wird wiederum in den Ausdruck im fünften Schritt eingesetzt und daraufhin wird erneut die Typregel T-APP angewendet. Das Resultat ist im sechsten Schritt dargestellt.

Die Anwendung der Typregel T-VAR auf den rechten Ausdruck im sechsten Schritt ergibt, dass T3 vom Typ Num ist. Dies wird nun in den linken Ausdruck des sechsten Schritts eingesetzt und abschließend wird die Typregel T-AND auf diesen Ausdruck angewandt. Die Typregel T-AND besagt, dass der Ausdruck AND vom Typ Bool nach Bool nach Bool ist.

Das Resultat im siebten Schritt sagt aus, dass AND vom Typ Bool nach Bool nach Bool ist. Es gibt zwei Variablen, die vom Typ Bool sind und das Resultat vom Ausdruck AND ist wiederum ein Bool. Dies ist ein Widerspruch zum Ergebnis des untypisierten Lambda-Kalküls in Abbildung 9, welches die Zahl 1 als Ergebnis von AND ergab.

2.3 Argumente für oder gegen Typsysteme

Eine große Auswahl von formalen Typsystemen mit korrespondierenden Algorithmen zur Typprüfung wurde entwickelt [11]. Sie können mit einfachen und strukturierten Typen, Datenabstraktion, Polymorphismus, Objektorientierung und Modularisierung umgehen [11]. Zur Verstärkung von übergeordneten modularen Eigenschaften und zum Schutz der Integrität von benutzerdefinierten Abstraktionen werden Typsysteme ebenfalls eingesetzt [30].

Nach [11] sollten Typsysteme bestimmte Eigenschaften besitzen. Es sollte einen Algorithmus geben, der nachweisen und melden kann, ob ein Programm typkorrekt ist oder nicht. Ein weiterer Punkt ist die Transparenz von Typsystemen. Ein Programmierer sollte die Möglichkeit haben, voraussagen zu können, ob ein Programm die Typprüfung durchführen wird. Die Typdeklarationen sollten so oft wie möglich statisch überprüft werden, aber andererseits auch dynamisch überprüft werden. Nach [30] und [27] ist der deutlichste Vorteil von statischen Typprüfungen die Möglichkeit des frühen Erkennens von Programmierfehlern. So können Fehler sorgfältiger während der Typprüfung zur Kompilierzeit aufgezeigt werden als während der Laufzeit, wo sie dann eventuell erst viel später erkannt werden. Auf diese Weise werden nach [10] Inkonsistenzen von Typen schon zur Kompilierzeit erkannt und dies garantiert ein typkonsistentes Programm. Typprüfer können auch als ein Wartungstool angesehen werden [30]. Die Änderung eines Datentyps reicht zunächst an einer Stelle im Programm aus. Nach Ausführung des Compilers zeigt dieser die weiteren Deklarationen an, die nach der Änderung nicht mehr vom Typprüfer akzeptiert werden. Somit ist es nicht notwendig, vor allem bei größeren Programmen, selbst nach den anderen Deklarationen zu suchen. Als weiteres Argument für Typsysteme nennt [30] die Typdeklaration in Methoden und Modulinterfaces, die gute Hinweise bezüglich des Verhaltens geben. Nach [10] lassen statische Typsysteme das Programm strukturierter aussehen und ermöglichen es auf diese Weise dem Programmierer das Programm leichter zu lesen und zu verstehen. Ein weiterer Vorteil ist nach [12], dass Typsysteme eine große Menge von Programmfehlern aufdecken können, wenn das Typsystem gut entwickelt ist. Auch das Sicherheitsrisiko können Typsysteme minimieren, aber nicht komplett lösen [12].

Neben den Vorteilen von Typsystemen gibt es einige Autoren, die Typsysteme als teilweise überflüssig und hindernd ansehen. In [44] und [10] sind die Autoren der Meinung, dass Programme mit statischen Typsystemen an Flexibilität und Ausdrucksstärke der Typen verlieren, es schwieriger ist mit ihnen zu arbeiten und sie weniger zugänglich sind für Veränderungen. In [16] wird dies bekräftigt durch den Autor, welcher der Ansicht ist, dass man mit der nicht typisierten Sprache Python komplexere Programme in kürzerer Zeit schreiben kann als mit statisch typisierten Programmiersprachen. Polymorphismus, Überladung und subtyping können ein System nach [26] zwar leistungsstärker machen, aber die Komplexität und Feinheit können nie die Flexibilität von nicht typisierten Programmiersprachen übersteigen. Dennoch können nach [15] durch dynamisch typisierte Programmiersprachen Typfehler erst zur Laufzeit erkannt werden. Dies kann zu schwerwiegenden Fehlern führen, die durch ein statisches Typsystem frühzeitig zur Kompilierzeit vermieden werden können.

Lange Zeit sah man dynamisch typisierte Programmiersprachen als langsam und unzuverlässig an. Sie waren nur für kleine Aufgaben nützlich [44]. Dies änderte sich durch die dynamische Entwicklung von Webseiten mit der dynamisch typisierten Sprache Perl, die aber nach [16] durch die unlesbare Syntax für größere Programme ungeeignet ist.

Das nachtägliche Einfügen eines Typsystems in eine Programmiersprache, die nicht für die Typprüfung entwickelt wurde, sieht der Autor in [30] als problematisch an. Ein Grund dafür ist, dass bestimmte Funktionen in Programmiersprachen ohne Typsysteme die Überprüfung von Typen erschweren oder nicht möglich machen. Ein anderer Faktor ist die kompliziertere Syntax von typisierten Programmiersprachen im Gegensatz zu den untypisierten Programmiersprachen. Nach [26] nutzen Programmiersprachen die Typprüfung, um Fehler während des Kompilierens zu entdecken und somit die Effizienz zur Laufzeit zu verbessern. Dennoch kann die Typprüfung nicht deaktiviert werden, falls sie nicht mehr benötigt wird oder hinderlich ist.

Ein Beispiel in [30] verdeutlicht nach Ansicht des Autors eine Schwäche von Typsystemen. Die folgende Programmzeile

if <complex test> then 5 else <type error>

wird bei der Typprüfung als Typfehler zurückgegeben, obwohl der Ausdruck <complex test> immer ein true zurückgeben wird. Die statische Überprüfung kann diesen Fall aber nicht beurteilen. In [16] wird nach Meinung des Autors eine weitere Schwachstelle von Typsystemen gezeigt. Das Kompilieren von statisch typisierten Programmiersprachen und somit auch die Typprüfung garantieren nur die Einhaltung der Syntax. Der Compiler gibt aber keine Garantie über die Korrektheit des Programmcodes. Nur die Testfälle, die die Korrektheit des Programms enthalten und überprüfen, geben eine Garantie über die Richtigkeit des Programms. Der Autor in [16] ist der Ansicht, dass es egal ist, wann die Typprüfung stattfindet. Das Wichtige ist, dass sie überhaupt stattfindet.

2.4 Codebeispiel für die Vor- und Nachteile von Typsystemen

In diesem Kapitel wird an einem kurzen Codebeispiel mit der Programmiersprache Java verdeutlicht, welche Vor- und Nachteile ein Typsystem hat. In Abbildung 11 ist die Methode setMatrikelnummer gezeigt, in der das Attribut matrikelnummer der Instanz Student, die eine Unterklasse der abstrakten Klasse Person ist, auf den Wert 2222514 gesetzt werden soll. Es wird als erstes überprüft, ob es sich bei der Variable p um eine Instanz der Klasse Student handelt. Wenn dies der Fall ist, wird im nächsten Ausdruck der Unterschied zwischen typisierten und untypisierten Programmiersprachen deutlich. Bei Java muss bei der Wertzuweisung auf den Typen Student explizit gecastet werden, da sonst ein Fehler zur Laufzeit gemeldet wird. Bei einer untypisierten Programmiersprache, wie zum Beispiel Groovy4, muss nicht auf einen Typen gecastet werden, was bei größeren Softwareprogrammen zu Zeitersparnissen beim Programmieren aufgrund von weniger Quellcode führen kann.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 11: Beispielcode für die Vor- und Nachteile von Typsystemen anhand von Java

Problematisch an dem Statement mit der Wertzuweisung ist, dass ein Tippfehler beim Schreiben des Quellcodes mit Groovy erst zur Laufzeit erkannt wird und so das Programm zum Absturz gebracht wird. In diesem Fall zeigt sich die Vorteilhaftigkeit eines Typsystems, da es diesen Fehler schon zur Kompilierzeit erkennt und es schon vor dem Ausführen des Programms behoben werden kann.

2.5 Typisierte und untypisierte Programmiersprachen

2.5.1 Python und Pylint

Python5 ist eine dynamisch typisierte Programmiersprache, die Anfang der 90er-Jahre von dem Holländer Guido von Rossum am Centrum voor Wiskunde en Infor-matica (CWI) in Amsterdam entwickelt wurde [17]. Die Programmiersprache vereint verschiede Programmierparadigmen. Es ist eine mächtige und unter anderem objektorientierte Programmiersprache, die durch die erweiterbare Standardbibliothek für viele praktische Programmierprojekte genutzt werden kann [41]. Die Plattformunabhängigkeit ist ein weiteres Merkmal dieser Programmiersprache.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 12: Beispielcode in der Programmiersprache Python

In Abbildung 12 wird eine Klasse Coursec mit der Methode dobeleidigung implementiert. Zu Beginn werden drei Klassen aus anderen Dateien mit dem Befehl from importiert. Anders als in Java dürfen die Klassennamen nicht mit dem Dateinamen übereinstimmen. In der ersten Zeile wird aus der Datei mit dem Namen Rowdy die Klasse Rowdyc importiert. Dies erfolgt analog für die beiden anderen Klassen in der zweiten und dritten Zeile. Das Statement def_init_(self): bezeichnet den Konstruktor in Python, der bei jeder Instanziierung eine Liste erzeugt. Der Methode dobeleidigung werden als Methodenparameter die Klasse selbst und zwei weitere Objekte übergeben. Das Schlüsselwort isinstance überprüft, ob ein Objekt eine Instanz einer bestimmten Klasse ist. In diesem Code überprüft es, ob das Objekt t1_ eine Instanz der Klasse Rowdyc ist. Anschließend werden noch einige Attribute der Instanzen aufgerufen.

Pylint6 ist ein Tool für Python, das den Code nach verschiedenen Kriterien untersucht. Hierzu gehört zum Beispiel die Variablenprüfung, die überprüft, ob es nicht definierte Variablen oder unbenutzte Variablen gibt. Eine weitere Option ist die Klassenprüfung, die kontrolliert, ob es Methoden ohne das Schlüsselwort self als erstes Argument oder überschriebene Methodensignaturen gibt. Es existiert auch ein Typprüfer. Seine Aufgabe ist das Finden von Fehlern mit Hilfe von Typinferenz.

2.5.2Ruby und DiamondbackRuby (DRuby)

Ruby7 ist nach [15] eine stark typisierte, objektorientierte Programmiersprache mit einer knappen und präzisen Syntax und einem flexiblen und dynamischen Typsystem. Sie wurde von Yukihiro „matz" Matsumoto entwickelt und er vermischte Teile anderer Sprachen (Perl, Smalltalk, Eiffel, Ada und Lisp) und formte daraus eine neue Programmiersprache, in der funktionale und imperative Programmierung ausbalanciert sind8. In Ruby ist alles ein Objekt und alle Objekte sind Klasseninstanzen [18].

Diamondback Ruby9 (DRuby) ist nach [15] ein statisches Typinferenzsystem für die Programmiersprache Ruby. Es wird dazu eingesetzt, Typfehler in Rubyprogrammen zu entdecken [18]. Die Typen werden im Code an die entsprechende Stelle eingetragen und DRuby überprüft diese Annotationen zur Laufzeit. Mit DRuby wurden einige Leistungstests durchgeführt und es fand mehrere Fehler, die normalerweise ohne DRuby zu Laufzeitfehlern geführt hätten [18].

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 13: Beispielcode in Ruby mit der Erweiterung DRuby

Im Beispielcode in der Abbildung 13 sind die beiden Klassen Course und Rowdy, die eine erweiterte Klasse von Teilnehmer ist, dargestellt. In der Klasse Rowdy wird durch die Schlüsselwörter attr_reader und attr_writer erlaubt, dass auf die Attribute dieser Klasse lesend bzw. schreibend zugegriffen werden darf. Die Methode initialize ist für das Erzeugen einer Instanz zuständig. In dieser Methode werden die Klassenattribute deklariert. Der Quellcode für die Erweiterung DRuby steht vor einer Methode und wird mit den Zeichen ##% gekennzeichnet. In den runden Klammern dahinter werden die Datentypen der Variablen, die an die Methode übergeben werden, definiert. In der Methode initialize sind es Zeichenketten und Zahlen. Das Wort hinter dem Pfeil am Ende der Zeile gibt den Rückgabetypen der Methode an. Das Schlüsselwort NilClass bedeutet, dass diese Methode keinen Rückgabewert hat. In der Klasse Course können der Methode doBeleidigung verschiedene Typen übergeben werden. Die Methodenparameter t1 und t2 können vom Typen Rowdy und Streber oder Rowdy und Normaler sein. In beiden Fällen hat die Methode keinen Rückgabewert. Innerhalb dieser Methode wird in mehreren if-Abfragen auf die Attribute der beiden übergebenen Instanzen t1 und t2 zugegriffen.

2.5.3 Purity

Eine weitere Programmiersprache wird in dem Experiment in [21] vorgestellt. Dabei handelt es sich um die Programmiersprache Purity, die speziell für das Experiment vom Autor in typisierter und untypiserter Version entwickelt wurde, damit die Probanden keine Vorkenntnisse über diese Programmiersprache besitzen und dies somit keinen Einfluss auf das Experiment hat. Purity ist eine objektorientierte Programmiersprache, die Ähnlichkeiten zu Smalltalk10, Ruby11 und Java12 hat. Bestandteile dieser Sprache sind unter anderem die einfache Vererbung und späte Bindung. Desweiteren hat sie vereinzelt Konzepte aus den Sprachen Smalltalk, Ruby und Java. Dazu gehören zum Beispiel Blöcke, wie sie in Smalltalk als closures vorkommen. Es wird nur die while-Schleife unterstützt, die durch eine Methode repräsentiert wird. Purity enthält eine einfache API mit 20 Klassen, die Integer, String oder Linked List unterstützt. Das Typsystem von Purity ähnelt stark dem von Java bis Version 1.4 und ist nicht generisch.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 14: Beispielcode für typisiertes und untypisiertes Purity aus [47]

In Abbildung 14 wird ein Beispielcode für untypisiertes und typisiertes Purity dargestellt. Beide Methoden erhalten einen Zahlenwert als Parameter und übergeben diesen an die lokale Variable locVar. Anschließend wird eine Schleife solange ausgeführt, bis die Variable locVar den Wert zehn annimmt. Die Variable locVar wird in jedem Schleifendurchgang um eins erhöht. Innerhalb der Schleife wird die Variable blockVar deklariert, welche den Wert der Variablen locVar zugewiesen bekommt. Als letzten Aufruf innerhalb der Schleife wird der Wert der Variable blockVar in einen String umgewandelt und anschließend ausgegeben. Im letzten Schritt gibt die Methode den Integerwert 42 zurück.

2.5.4 Java und Groovy

Die Programmiersprache Java13 ist eine statisch typisierte objektorientierte Programmiersprache. Groovy ist die dynamisch typisierte Variante von Java, die jedoch auch statische Typisierung ermöglicht [43]. Der große Unterschied von Groovy zu Java ist, dass es in Groovy keine primitiven Datentypen, wie zum Beispiel boolean, char, byte, int, gibt [6]. „In Groovy gibt es ausschließlich Objekte. Selbst jede Zahl und jeder boolesche Wert ist ein Objekt"[6]. Dennoch können nach [43] primitive Datentypen in Groovy vorkommen, denn Groovy kooperiert mit anderen JavaKomponenten, welche gewöhnlich nicht auf den Gebrauch von primitiven Datenelementen in ihren Schnittstellen verzichten. „Groovy erlaubt also die Deklaration von Klassen-Membern und Array-Elementen sowie Methoden- und Konstruktorparameter mit primitiven Typen"[43]. Wenn aber auf die primitiven Datentypen in irgendeiner Weise zugegriffen wird, müssen diese mit Hilfe von Wrapper-Klassen in ein Objekt umgewandelt werden [6]. Groovy wandelt primitive Datentypen automatisch in Objekte um, und zwar sowohl bei der Verwendung als auch bei der Deklaration von Typen [6]. Bei dem folgenden Ausdruck „int wert = 42 wird sowohl der Wert 42 direkt in ein Integer-Objekt verwandelt als auch die Variable wert in eine Referenz auf einen Integer"[6].

2.6 Zusammenfassung

In diesem Kapitel wurde eine kurze Einführung in das Thema Typsysteme gegeben. In diesem Zusammenhang wurde ein Typsystem formal vorgestellt in Form des Lambda-Kalküls und die Anwendung und der Nutzen dessen anhand eines Beispiels erläutert. Desweiteren wurden einige ausgewählte Programmiersprachen und Ergänzungen dazu vorgestellt, um einen Überblick über typisierte und untypisierte Programmiersprachen zu geben.

In dieser Bachelorarbeit wird der Nutzen eines Typsystems mit Hilfe eines kontrollierten Experiments untersucht. Hierzu wird ein kontrolliertes Experiment mit verschiedenen Aufgaben entwickelt. Diese Aufgaben sollen von Probanden sowohl mit einer typisierten als auch mit einer untypisierten Programmiersprache gelöst werden. Auf diese Weise wird empirisch überprüft, ob ein Typsystem einen Vorteil bei der Entwicklung von kleinen Softwareprogrammen liefert.

Eine kurze Einführung in die empirische Softwareforschung wird im folgenden Kapitel gegeben. Dabei werden die verschiedenen Möglichkeiten von empirischen Studien vorgestellt. Hierzu gehören insbesondere die kontrollierten Experimente, da eines in dieser Bachelorarbeit entwickelt wird. Desweiteren werden verschiedene Möglichkeiten zur Messung in empirischen Studien aufgezeigt sowie die Vor- und Nachteile der Verwendung von Studenten als Probanden.

3 Empirische Softwareforschung

Dieses Kapitel gibt einen kurzen Einblick in die empirische Softwareforschung. Nach einer kurzen Einführung über empirische Studien wird dargestellt, was unter einem kontrollierten Experiment verstanden wird. In diesem Zusammenhang werden Merkmale zur Evaluation von Software und verschiedene Messtechniken für empirische Studien vorgestellt und erläutert. Als letzten Punkt wird auf die Klassifizierung von Probanden eingegangen und die Vor- und Nachteile der Verwendung von Studenten vorgestellt.

3.1 Einführung

Empirische Studien spielen eine wichtige Rolle in der Entwicklung von Softwaretechnik [5]. Sie ermöglichen das Überprüfen von Theorien, wichtige Variablen zu identifizieren und Modelle zu erstellen, die durch empirische Aussagen unterstützt werden. Die betrachteten Prozesse sind zu komplex, um sie mathematisch zu analysieren und können deshalb nur unvollständig erfasst werden [31]. Durch die Empirie in der Softwaretechnik lässt sich ein Verständniswissen über den Vorgang der Softwarekonstruktion erhalten. So können zum Beispiel die Vor- und Nachteile einer Methode verdeutlicht werden.

In [25] werden empirische Studien in quantitative und qualitative Studien unterteilt. Die quantitative Forschung hat das Ziel, numerische Beziehungen zwischen verschiedenen Variablen oder Alternativen unter Einbeziehung von Versuchen zu untersuchen. Qualitative Studien hingegen beobachten und forschen Objekte in ihren natürlichen Situationen. Sie versuchen Gegebenheiten zu untersuchen, die von Menschen zum Forscher gebracht werden und auf Erklärungen basieren [46]. Dabei sind die Menschen die Subjekte, deren Verhalten in einer bestimmten Situation untersucht wird.

In [46] und in [14] werden drei verschiedene Arten von Strategien beschrieben, die für empirische Softwareforschung eingesetzt werden können. Eine Studie untersucht vergangene Ereignisse oder Projekte. Dabei werden Daten analysiert oder Menschen durch ein Interview oder Fragebogen befragt. Es sollte aber keine Möglichkeit geben die Ausführung oder Messung der Studie zu beeinflussen. Eine Fallstudie wird dazu genutzt, Projekte, Aktivitäten oder Aufgaben zu beobachten. Die Daten werden für einen bestimmten Zweck gesammelt, zum Beispiel für spätere statistische Analysen. Auf diese Weise sollen spezifische Attribute oder Beziehungen zwischen verschiedenen Attributen aufgezeigt werden. Die dritte Art von Strategie sind die kontrollierten Experimente, auf die im folgenden Kapitel 3.2 näher eingegangen wird, um eine kurze Einführung in dieses Thema zu geben, da im Rahmen dieser Bachelorarbeit eines entwickelt wird.

[...]


1 http://java.com/de/

2 http://www.python.de/

3 http://groovy.codehaus.org/

4 http://www.python.org/about/

5 http://www.logilab.org/project/pylint

6 http://www.ruby-lang.org/de/

7 http://www.ruby-lang.org/de/about/

8 http://www.cs.umd.edu/projects/PL/druby/

9 http://www.smalltalk.org/main/

10 http://www.ruby-lang.org/de/

11 http://java.com/de/

12 http://java.com/de/

13 http://groovy.codehaus.org/

Ende der Leseprobe aus 85 Seiten

Details

Titel
Messung des Einflusses von Typsystemen auf die Entwicklungszeit von kleinen Softwareprojekten anhand von Java und Groovy
Hochschule
Universität Duisburg-Essen
Note
1,3
Autor
Jahr
2010
Seiten
85
Katalognummer
V191525
ISBN (eBook)
9783656164951
ISBN (Buch)
9783656165064
Dateigröße
1385 KB
Sprache
Deutsch
Schlagworte
messung, einflusses, typsystemen, entwicklungszeit, softwareprojekten, java, groovy, empirisch, empirical, Probanden, statisch, dynamisch
Arbeit zitieren
Andreas Stuchlik (Autor), 2010, Messung des Einflusses von Typsystemen auf die Entwicklungszeit von kleinen Softwareprojekten anhand von Java und Groovy, München, GRIN Verlag, https://www.grin.com/document/191525

Kommentare

  • Noch keine Kommentare.
Im eBook lesen
Titel: Messung des Einflusses von Typsystemen auf die Entwicklungszeit von kleinen Softwareprojekten anhand von Java und Groovy



Ihre Arbeit hochladen

Ihre Hausarbeit / Abschlussarbeit:

- Publikation als eBook und Buch
- Hohes Honorar auf die Verkäufe
- Für Sie komplett kostenlos – mit ISBN
- Es dauert nur 5 Minuten
- Jede Arbeit findet Leser

Kostenlos Autor werden