Sprachübergreifendes Refaktorisieren zwischen Ruby und Java - FernUniversität in Hagen Fakultät für Mathematik und Informatik Lehrgebiet ...

Die Seite wird erstellt Nicolas Ruf
 
WEITER LESEN
Sprachübergreifendes Refaktorisieren zwischen Ruby und Java - FernUniversität in Hagen Fakultät für Mathematik und Informatik Lehrgebiet ...
FernUniversität in Hagen
      Fakultät für Mathematik und Informatik
          Lehrgebiet Programmiersysteme
               Prof. Dr. F. Steimann

Sprachübergreifendes Refaktorisieren
      zwischen Ruby und Java

                  Bachelorarbeit

                   Antje Osten
              Matrikelnummer q6632386
                    2. April 2012
Sprachübergreifendes Refaktorisieren zwischen Ruby und Java - FernUniversität in Hagen Fakultät für Mathematik und Informatik Lehrgebiet ...
Erklärung

Hiermit erkläre ich, dass ich diese Abschlussarbeit selbstständig verfasst, noch nicht
anderweitig für Prüfungszwecke vorgelegt, keine anderen als die angegebenen Quellen
und Hilfsmittel benutzt sowie wörtliche und sinngemäße Zitate als solche gekennzeichnet
habe.

Berlin, 2. April 2012

Antje Osten
Sprachübergreifendes Refaktorisieren zwischen Ruby und Java - FernUniversität in Hagen Fakultät für Mathematik und Informatik Lehrgebiet ...
Zusammenfassung
Refactoring (Refaktorisierung) beschreibt eine Strukturänderung von Programm-Quell-
texten unter Beibehaltung des beobachtbaren Verhaltens. Refaktorisierungen sind Teil
der Implementierungsphase. Werkzeuge sollen helfen, die Entwickler und Entwicklerin-
nen in der Komplexität des Refaktorisierungsvorgangs zu unterstützen. Bisher exis-
tieren entwicklungsumgebungs- und programmiersprachenabhängige Refaktorisierungs-
werkzeuge. Auf Basis des Frameworks Refacola wird ein sprachübergreifender Ansatz
diskutiert, in dem nicht nur mehrere Sprachen in einem Projekt erlaubt sind, sondern
darüber hinaus Ideen für eine Refaktorisierung zwischen statisch getypten und dynami-
schen Programmiersprachen aufgezeigt und programmiertechnisch konkretisiert werden.
Sprachübergreifendes Refaktorisieren zwischen Ruby und Java - FernUniversität in Hagen Fakultät für Mathematik und Informatik Lehrgebiet ...
Inhaltsverzeichnis

   Erklärung                                                                                               2

   Zusammenfassung                                                                                         3

1 Einleitung                                                                                               6
  1.1 Refaktorisierung – ein in der Softwareentwicklung integrierter Prozess .                        .    7
  1.2 Werkzeuge für den Refaktorisierungsprozess . . . . . . . . . . . . . . . .                      .    9
       1.2.1 Refaktorisierungswerkzeuge aus historischem Blickwinkel . . . .                          .   10
       1.2.2 Refaktorisierungswerkzeuge als eigenes Fachgebiet . . . . . . . .                        .   12
  1.3 Flexibilität durch eine programmiersprachen-übergreifende Entwicklung                           .   13
       1.3.1 Vorteile der Interaktion mit dynamischen Programmiersprachen .                           .   14
       1.3.2 Java Virtual Machine als Basis mehrerer Programmiersprachen .                            .   15
  1.4 Sprachübergreifende Refaktorisierungswerkzeuge . . . . . . . . . . . . .                        .   17

2 Entwicklungsumgebung und mehrsprachige Projekte                                  19
  2.1 Eclipse Dynamic Language Toolkit . . . . . . . . . . . . . . . . . . . . . . 19
  2.2 Verbindung eines Java- und Ruby-Projekts in der Eclipse . . . . . . . . . 19

3 Interoperabilität der Programmiersprachen Ruby und Java                                                 21
  3.1 Einordnung der Programmiersprache Ruby . . . . . . . . .            .   .   .   .   .   .   .   .   21
       3.1.1 Die Ideale des Ruby-Erfinders . . . . . . . . . . . . .      .   .   .   .   .   .   .   .   21
       3.1.2 Merkmale der Programmiersprache Ruby . . . . . .             .   .   .   .   .   .   .   .   22
       3.1.3 Dynamische Aspekte der Programmiersprache Ruby               .   .   .   .   .   .   .   .   25
  3.2 Methodenaufrufe zwischen Ruby und Java . . . . . . . . . .          .   .   .   .   .   .   .   .   27
       3.2.1 Methodenaufruf von Java nach Ruby . . . . . . . . .          .   .   .   .   .   .   .   .   27
       3.2.2 Methodenaufruf von Ruby nach Java . . . . . . . . .          .   .   .   .   .   .   .   .   28
  3.3 Konvertierungsregeln . . . . . . . . . . . . . . . . . . . . . .    .   .   .   .   .   .   .   .   28
       3.3.1 Konvertierungsregeln für Sichtbarkeiten . . . . . . .        .   .   .   .   .   .   .   .   28
       3.3.2 Regeln für die Abbildung von Datentypen . . . . . .          .   .   .   .   .   .   .   .   29
  3.4 Bedingungen für eine Refaktorisierung . . . . . . . . . . . .       .   .   .   .   .   .   .   .   30
       3.4.1 Sichtbarkeiten sind nicht relevant . . . . . . . . . . .     .   .   .   .   .   .   .   .   31
       3.4.2 Getter und Setter . . . . . . . . . . . . . . . . . . .      .   .   .   .   .   .   .   .   31
       3.4.3 to_s . . . . . . . . . . . . . . . . . . . . . . . . . . .   .   .   .   .   .   .   .   .   32

4 Refacola – Constraint-Sprache und Framework                                          34
  4.1 Die Sprache Refacola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
      4.1.1 Sprachdefinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
      4.1.2 Definition der Constraintregeln . . . . . . . . . . . . . . . . . . . . 36

                                            4
Sprachübergreifendes Refaktorisieren zwischen Ruby und Java - FernUniversität in Hagen Fakultät für Mathematik und Informatik Lehrgebiet ...
4.1.3 Definition von Refaktorisierungen . . . . . . . . . . . . . . . . . . . 37
   4.2   Definition eines Verbots für das Refaktorisieren sprachübergreifender Me-
         thodenaufrufe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

5 Codeanalyse als Voraussetzung für Refaktorisierung                                                                         40
  5.1 Statische Codeanalyse . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                        . 42
      5.1.1 Exkurs – Mehrdeutigkeit in Ruby-Quelltexten . . . . . . . . . . .                                              . 42
      5.1.2 Mehrdeutigkeit bei Sprachübergriffen zwischen Ruby und Java .                                                  . 45
      5.1.3 Endgültiges Versagen der Typanalyse bei dynamischen Klassenna-
              men . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      .   48
      5.1.4 Vorläufiger Ablauf einer statischen Codeanalyse . . . . . . . . . .                                            .   48
  5.2 Test-Suite zum Sammeln von Informationen für eine Refaktorisierung .                                                 .   49
      5.2.1 Test-Suite mit modifiziertem JRuby-Interpreter . . . . . . . . . .                                             .   51
      5.2.2 Test-Suite mit Instrumentierung . . . . . . . . . . . . . . . . . .                                            .   52
  5.3 Ausblick auf eine sinnvollen Verbindung der unterschiedlichen Ansätze .                                              .   52

6 Beschreibung der Software                                                                                                    54
  6.1 Das Plugin AST4CongenJRubyView . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   54
  6.2 XRefact . . . . . . . . . . . . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   55
      6.2.1 Modifizierter JRuby-Interpreter . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   57
      6.2.2 Modifiziertes Refacola-Framework           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   58
      6.2.3 Instrumentation . . . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   58

A Auflistung, Installations- und Bedienungsanleitung der                   beigelegten                 Software                60
  A.1 Beigelegte Software . . . . . . . . . . . . . . . . . .              . . . . . . .               . . . . . .             60
  A.2 Installation und Bedienung . . . . . . . . . . . . .                 . . . . . . .               . . . . . .             60
       A.2.1 XRefact . . . . . . . . . . . . . . . . . . . .               . . . . . . .               . . . . . .             60
       A.2.2 Plugin AST4CongenJRubyView . . . . . .                        . . . . . . .               . . . . . .             61

B Abkürzungen                                                                                                                  62

C Liste bekannter Refaktorisierungswerkzeuge                                                                                   63

Abbildungsverzeichnis                                                                                                          64

Listingverzeichnis                                                                                                             65

Literaturverzeichnis                                                                                                           66

                                             5
Sprachübergreifendes Refaktorisieren zwischen Ruby und Java - FernUniversität in Hagen Fakultät für Mathematik und Informatik Lehrgebiet ...
1 Einleitung

Refactoring (Refaktorisierung) gilt heute als eine grundlegende Disziplin zur Realisierung
eines guten Designs. Der Begriff steht für eine manuell oder automatisiert durchgeführte
Strukturänderung von Programm-Quelltexten unter Beibehaltung des beobachtbaren
Verhaltens der jeweiligen Software (vgl. u. a. [Ste10], [Fow05]). Andere variable Faktoren,
beispielsweise eine veränderte Laufzeit, müssen mit vorher spezifizierten Anforderungen
an die Software abgeglichen werden.
   Als Ergebnis des überarbeiteten Designs sollen, neben einer verbesserten Lesbarkeit
und Verständlichkeit der Quelltexte, Module leichter isolierbar und damit besser testbar
werden, wodurch der Aufwand für Fehleranalysen gesenkt wird und Softwaresysteme
leichter wartbar werden. Zusätzlich wird für die Zukunft erhofft, durch Refaktorisierung
existierende Programme problemlos erweitern zu können.
   Hinsichtlich der erwarteten Ergebnisse ist eine umfassende Auseinandersetzung mit
dem Begriff Refaktorisierung im Hinblick auf heutige Softwareprojekte sinnvoll. Ziel muss
sein, die Integration eines Refaktorisierungsvorgangs in die konkrete Softwareentwicklung
möglichst einfach zu gestalten. Dafür bietet sich eine Automatisierung in Form von
Refaktorisierungswerkzeugen an.
   In der vorliegenden Arbeit soll ausgelotet werden, wie automatisierte Refaktorisierun-
gen für programmiersprachenübergreifende Softwareprojekte erfolgen können. Der Fokus
wird auf solche Stellen gelegt, an denen die unterschiedlichen Programmiersprachen mit-
einander interagieren.
   Jedoch muss deutlich unterschieden werden zwischen der Zusammenarbeit von aus-
schließlich solchen Programmiersprachen, die ein statisches Typsystem besitzen, und der
Zusammenarbeit von statisch typgeprüften Sprachen mit Programmiersprachen, die hier
als zweckmäßige Einordnung mit dem Begriff „dynamisch“ belegt werden.
   Statisch typgeprüfte Programmiersprachen, in denen die Typregeln schon während
des Übersetzungsvorgangs kontrolliert werden, harmonieren besser mit dem Prozess des
Sammelns von notwendigen Informationen für einen Refaktorisierungsvorgang als dyna-
mische Sprachen.
   Beim Refaktorisieren werden Informationen genutzt, die in statisch typgeprüften Spra-
chen nahezu vollständig (mit wenigen Ausnahmen) zur Verfügung stehen. Beispielsweise
kann die Bindung einer Methode an ihre Definition statisch abgeleitet werden.
   Dies ist in einer nicht statisch typgeprüften Sprache ungleich schwerer. Hier kann zwar
die Reihenfolge der Anweisungen abgebildet werden, aber spätestens bei Aufruf einer
Methode, die zur Laufzeit an jeweils zwei unterschiedliche Objekte gebunden werden
kann, sind die gefundenen Informationen über das Programm nicht mehr eindeutig.
Damit stehen die notwendigen Informationen für eine automatisierte Refaktorisierung
nicht in der Klarheit zur Verfügung, wie es in statisch typgeprüften Sprachen der Fall
ist.

                                            6
Das Thema dieser Ausarbeitung wird eingegrenzt auf solche sprachübergreifende Pro-
jekte, in denen statisch typisierte und dynamische Programmiersprachen in einer Soft-
ware genutzt werden.
   In der Einleitung wird zuerst skizziert, zu welchem Zeitpunkt Refaktorisierungen ein-
setzen sollen. Hier wird der Refaktorisierungsprozess auf startbare Programme mit be-
obachtbarem Verhalten eingegrenzt. Weiter werden exemplarisch Refaktorisierungswerk-
zeuge historisch verfolgt, um Ideen und Ansätze, aber auch existierende Schwierigkeiten
solcher Werkzeuge in die weitere Diskussion mit einbeziehen zu können. Zur Konkretisie-
rung des Themas der vorliegenden Arbeit wird dann eine aktuelle Forschung1 vorgestellt,
in der Refaktorisierungswerkzeuge als eigenes Forschungsgebiet diskutiert werden. Dieser
Ansatz wird als Grundlage der Ausarbeitung und der beigelegten Software genutzt. Als
Motivation für die Notwendigkeit, Refaktorisierungswerkzeuge für sprachübergreifende
Projekte zu entwickeln, werden Vorteile solcher mehrsprachigen Projekte beschrieben.
Am Ende der Einleitung werden die herangezogene Entwicklungsumgebung und die ver-
wendeten Programmiersprachen vorgestellt und der Aufbau der Ausarbeitung skizziert.

1.1 Refaktorisierung – ein in der Softwareentwicklung integrierter Prozess

Bei klassischen Vorgehensmodellen der Softwareentwicklung kann Flexibilität oft schwer
realisiert werden. Es lässt sich kaum verhindern, dass in Projekten mit einem anfäng-
lich ausgefeilten Entwurf eine nachträgliche Änderung der Spezifikation ein notwendiges
Redesign mit sich bringt, das im schlechtesten Fall dazu führt, dass große Teile neu
programmiert werden müssen oder das gesamte Produkt neu entworfen werden muss.
Workarounds, also Programmcodes, durch die ein Projekt nachträglich an neue Ziele
angepasst werden soll, machen den Quellcode oft unübersichtlich und lassen ihn mit der
Zeit strukturlos und unwartbar werden. Martin Fowler, der unterstützt von Kent Beck,
William Opdyke, Don Roberts, John Brant und Erich Gamma im Jahr 1999 einen Satz
von Refaktorisierungsregeln aus dem Erfahrungsfundus von Entwicklern und Entwickle-
rinnen katalogisiert hat, folgert hieraus: „Ohne Refaktorisieren zerfällt das Design eines
Programms mit der Zeit.“ [Fow05, S. 43]
   Eine stufenweise Refaktorisierung im Nachhinein könnte in dieser Phase helfen. Jedoch
existiert in diesem Zusammenhang die Angst vor hohen Kosten und großem Zeitaufwand.
   Andere Softwareentwicklungsprozesse, die unter dem Begriff Agile Softwareentwick-
lung zusammengefasst werden, integrieren von vornherein den Prozess der Refaktorisie-
rung in den Ablauf der Softwareerstellung. Im agilen Ansatz wird es sogar als Ziel for-
muliert, Software flexibel zu halten (vgl. [Bec00]). In der Phase der Entwicklung sollen
fortwährend neue Ideen für ein Produkt einfließen können. Refaktorisierung des Quell-
codes setzt dem agilen Ansatz folgend nicht erst nach Fertigstellen eines Programms
ein. Genauso wenig wird die Strukturänderung von Codes als paralleles Vorhaben neben
(verstanden als abgelöst von) der konkreten Softwareentwicklung betrachtet. Refaktori-
sierung gilt im agilen Ansatz als ein elementarer Prozess, der (eng verknüpft) mit zur

 1
     Vgl. http://www.fernuni-hagen.de/ps/prjs

                                                7
Entwicklung gehört. In der Praxis ist der Refaktorisierungsprozess mit der Implemen-
tierung kombiniert.
   Um den Begriff Refaktorisierung inhaltlich zu konkretisieren, soll noch einmal Fow-
ler herangezogen werden. Er beschreibt Refaktorisierung zum einen mit Fokus auf den
Gebrauch als Substantiv (Refaktorisierung) und zum anderen auf den Gebrauch als
Verb (refaktorisieren): Refaktorisierung in Substantivform wurde zuvor schon aufge-
griffen als eine Änderung an der internen Struktur zur besseren Verständlichkeit (vgl.
[Fow05, S. 41]). Erweiternd fügt Fowler dem Begriff eine Aktion hinzu: „Refaktorisie-
ren(Verb): Eine Software umstrukturieren, ohne ihr beobachtbares Verhalten zu ändern,
indem man eine Reihe von Refaktorisierungen anwendet.“ [Fow05, ebd.])
   Fowler zeigt damit eine Technik. Die Änderungen sollen in kleinen, abgeschlossenen
Einheiten vorgenommen werden. Zwingend ist, nach jedem einzelnen Schritt zu testen,
ob das Verhalten gleich geblieben ist. Mit dieser Technik weist Fowler den Weg, den Re-
faktorisierungsprozess manuell – parallel zur Softwareentwicklung – vorzunehmen. Mit
Hilfe eines Katalogs von Regeln kann das Design nun verbessert werden, nachdem größere
Teile der Software bereits implementiert sind. Fowler stellt sich damit dem angesproche-
nen Problem, dass ein Softwaredesign vorher zwar geplant wird, im Zuge der Imple-
mentierung jedoch oft verloren geht. Spätestens dann, wenn sogenannte „Code Smells“
(vgl. [Fow05, S. 67-82] oder als erweiterter Katalog [Mar09, S. 337-372]) in einer Soft-
ware entdeckt würden, könne durch ein schrittweises Refaktorisieren ermöglicht werden,
die Struktur im Nachhinein zu verbessern, und die Wahrscheinlichkeit gering gehalten
werden, dabei neue Fehler einzuführen. Fowler geht sogar soweit, dass er behauptet, ein
mit einem chaotischen Design begonnenes Projekt könne durch das schrittweise Refak-
torisieren radikal verbessert werden, und spitzt diese Aussage noch einmal darauf zu,
dass ein von vornherein vollständig durchdachtes Design gar nicht notwendig sei: „Sie
werden feststellen, dass das Design, anstatt vollständig vorher zu erfolgen, kontinuierlich
während der Entwicklung stattfindet.“ [Fow05, S. xix]
   Fowler formuliert dann für den aktiven Refaktorisierungsvorgang (refaktorisieren) die
Aussage: „Refaktorisieren ist ein Prozess, ein Softwaresystem so zu verändern, dass das
externe Verhalten nicht geändert wird, der Code aber eine bessere interne Struktur
erhält.“ [Fow05, S. xviii]
   Stefan Roock und Martin Lippert fassen die Refaktorisierung ebenso als kontinuierli-
ches Entwerfen auf und sehen im Jahr 2004 darüber hinaus Refaktorisierung mittlerweile
als festen Bestandteil, vor allem in groß angelegten, agilen Entwicklungsprojekten: „Mit
dem ständigen Refactoring der Software gehen wir in die entgegengesetzte Richtung: Re-
factoring und Entwerfen werden zum Bestandteil der täglichen Entwicklungsarbeit. Es
wird damit nicht weniger entworfen als in klassischen Projekten. Die Entwurfsaufwände
werden lediglich gleichmäßiger über den Entwicklungsprozess verteilt.“ [RL04, S. 13]
   Roock und Lippert möchten ein – eventuell durch Schwachstellen notwendig geworde-
nes – Redesign des Gesamtsystems verhindern. Sie fordern, dass eine Refaktorisierung
der Software in den Entwicklungsprozess mit eingebaut wird. Bei auftretenden Schwach-
stellen soll, ebenso wie bei Fowler, in möglichst kleinen Schritten restrukturiert werden
(vgl. [RL04, S. 18-25]).
   Als Prämisse der vorliegenden Ausarbeitung soll die Implementierungsphase als zen-

                                            8
trales „Paket“ verstanden werden, in dem das Refaktorisieren, das Programmieren und
darüber hinaus das heute vor allem in agilen Projekten eingesetzte Testen von Mo-
dulen2 und Verhalten (Behavior Driven Development3 ) gebündelt wird. Damit entfällt
jegliche Idee einer eigenständigen „Phase der Refaktorisierung“, die zusätzlich Zeit for-
dern würde. Aus dem agilen Ansatz wird also übernommen, dass das Refaktorisieren
einhergehend mit der Implementierung stattfindet. Aus der Perspektive des oder der
Programmierenden wird der Refaktorisierungsprozess in dieser Phase in einen Hand-
lungsablauf eingefasst und der Vorgang systematisiert.
  Abgegrenzt wird der Begriff Refaktorisierung von seiner Verwendung in Phasen der
Software-Erstellung, in denen noch kein sichtbares Verhalten existiert, beispielsweise,
wenn ein Programm noch nicht lauffähig und damit der Programmablauf noch nicht
beobachtbar ist. Damit fallen Designänderungen vor der Implementierung genauso we-
nig unter den hier verwendeten Begriff von Refaktorisierung wie Architekturänderun-
gen. Grenzfälle, beispielsweise eine Änderung von Quellcode, ohne dass ein Programm
lauffähig ist, werden durch die klare Definition herausgefiltert, dass das Verhalten be-
obachtbar sein muss. Strukturänderungen am Ende der Implementierung werden durch
die vorherige Aussage eingeschlossen.
  Refaktorisierung impliziert nicht zwingend das formulierte Ziel einer Strukturverbes-
serung. Nur die Programmierenden selbst können entscheiden, welche Umstrukturierun-
gen sinnvoll sind im Hinblick auf die von ihnen erstellte Software. Anhaltspunkte hierfür
können sich aus den festgehaltenen Erfahrungen ergeben, die in den letzten Jahren von
unterschiedlichen Autoren und Autorinnen veröffentlicht wurden. Hier existieren auto-
matisierte Ansätze, beispielsweise Smell Detection (vgl. u. a. [Sli05]), auf die an dieser
Stelle jedoch nicht weiter eingegangen wird.

1.2 Werkzeuge für den Refaktorisierungsprozess

Selbst wenn zu jeder Zeit eine wünschenswerte Strukturänderung von Quellcode erfolgt,
die dann im positiven Fall zur Übersichtlichkeit beiträgt, zeigt sich in einer manuellen
Refaktorisierung das Problem, dass Abhängigkeiten im Code nicht unmittelbar und da-
mit mühelos von den Programmierenden nachverfolgbar sind. Denn den Überblick über
Quellcode zu behalten, der auf mehrere Dateien in unterschiedlichen Ordnern bzw. Pa-
keten verteilt und dazu mit programmiersprachenspezifischen Sichtbarkeitsregeln belegt
wurde, ist spätestens in einem Team von mehreren Entwickelnden schwer zu leisten.
Mit anderen Worten: Manuelles Refaktorisieren ist fehleranfällig. Durch den skizzier-
ten Ablauf, die Struktur in kleinen Schritten zu ändern, und durch die jeweils sofort
notwendigen Tests auf semantische Gleichheit ist eine manuelle Refaktorisierung zudem
sehr zeitaufwändig. Wie schon durch den Begriff „automatisiert“ angedeutet, wurden
und werden daher im Zuge der Diskussion um Refaktorisierung Werkzeuge entwickelt,
die den Prozess der Strukturänderung von Quellcode unterstützen. Programmierenden
werden für Änderungswünsche automatisierte Angebote gemacht oder die gewünschte

 2
     Exemplarisch http://www.junit.org, http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/
 3
     Exemplarisch http://cukes.info

                                               9
Änderung vollständig übernommen.
  Die im Folgenden exemplarisch herausgegriffenen frühen Werkzeuge für eine Ände-
rung der Struktur eines Quellcodes sollen den Blick auf die Entstehung heutiger und
kommender Automatisierungen von Refaktorisierungsvorgängen lenken, mit dem Ziel,
Refaktorisierungswerkzeuge im Kontext sprachübergreifender Projekte im weiteren Ver-
lauf einordnen und damit Möglichkeiten und Grenzen eines solchen Werkzeugs auch
historisch diskutieren zu können.
  Die historische Sicht führt darüber hinaus zu der Frage nach dem Stand der Entwick-
lung heutiger Werkzeuge. Denn die zuvor skizzierten Probleme der Fehleranfälligkeit
und natürlich auch des immensen Zeitaufwandes durch manuelles Refaktorisieren ma-
chen es notwendig, existierende Werkzeuge, wenn möglich, weiter zu entwickeln und zu
verbessern. In einem Folgeabschnitt wird dazu der für diese Ausarbeitung herangezogene
Ansatz vorgestellt, in dem nach einem einheitlichen Standard für Refaktorisierungswerk-
zeuge gesucht wird.

1.2.1 Refaktorisierungswerkzeuge aus historischem Blickwinkel
Nach einer Abhandlung über Strukturierungswerkzeuge für Softwaresysteme von Tho-
mas Dudziak und Jan Wloka aus dem Jahre 2002 ist der Anfang der Auseinandersetzung
mit dem skizzierten Begriff Refaktorisierung zeitlich zumindest ab Anfang der 70er Jahre
einzuordnen, als die Diskussion über das GOTO-Statement in imperativen Programmen
ihren Höhepunkt hatte, wenn nicht sogar davor: „Refactoring is not new - in fact it has
been done unknowingly at least since the introduction of structured programming, if not
earlier.“ [DW02, S. 10]
   Sie begründen diese Aussage damit, dass die Sprunganweisung GOTO ersetzt wurde,
ohne das Verhalten der jeweiligen Software zu verändern.
   Diese Regel, Restrukturieren ohne Verhaltensänderung, entspricht zwar dem heute ein-
gegrenzten Begriff der Refaktorisierung. Ob damalige Restrukturierungen im Zuge der
Diskussion um GOTO jedoch schon als ein Anfang von Refaktorisierung verstanden wer-
den kann, ist m. E. zu diskutieren. Denn Dudziak und Wloka ignorieren an dieser Stelle,
dass es damals im Grunde nur um die Beweisführung ging, dass die Sprunganweisung
GOTO durch die Kontrollstruktur Schleife, ggf. durch die Einführung von booleschen
Variablen, ersetzt und dann mit dem endgültig veränderten Code weiter gearbeitet wer-
den kann. Den Quellcode selbst leserlicher zu gestalten, war für diesen Beweis nicht
relevant: „In [2] Guiseppe Jacopini seems to have proved the (logical) superfluousness
of the go to statement. The exercise to translate an arbitrary flow diagram more or less
mechanically into a jump-less one, however, is not to be recommended. Then the resul-
ting flow diagram cannot be expected to be more transparent than the original one.“
[Dij68]
   Jedoch soll im Hinblick auf eine automatisierte Refaktorisierung die Aufmerksam-
keit darauf gelenkt werden, dass zu jener Zeit im Zuge der Auseinandersetzung mit
Restrukturierungsprozessen erste Werkzeuge entwickelt wurden, die eine automatisier-
te Strukturänderung boten. Eines der ersten entstandenen Werkzeuge war Mitte der
70er Jahre die „Structuring Engine“ für die Sprache Fortran, die jedoch den Objekt-

                                          10
code und die Laufzeit der Software stark anwachsen ließ. Stefan Eickner und Thomas
Schnieder vermuten dazu in einem Arbeitsbericht aus dem Jahr 1992, dass sich das
Werkzeug deswegen wahrscheinlich nicht durchsetzen konnte: „Das Tool zerlegt das Pro-
gramm in Prozedurblöcke und ersetzt vorwärtsgerichtete Goto-Anweisungen durch If-
then-else-Konstrukte, rückwärtsgerichtete Goto-Anweisungen durch Do-until-Schleifen
sowie Goto-Anweisungen mit Sprungzielen außerhalb der Blöcke durch Prozeduraufrufe.
Der Restrukturierungsprozess lässt den Objektcode um 10% - 40% und auch die Lauf-
zeit des Programmes um bis zu 8% anwachsen. Dies ist als ein wesentlicher Grund dafür
anzusehen, daß sich das Tool in der Praxis nicht durchgesetzt hat.“ [ES92, S. 17-18]
  Ab Mitte der 80er Jahre wurde die Restrukturierung von prozeduralen und objektori-
entierten Softwaresystemen weiterverfolgt.
  Für imperative Programmiersprachen untersuchte William G. Griswold das Restruk-
turieren ohne Verhaltensänderung. Er verwendete die semi-funktionale Programmier-
sprache Scheme4 . Die Programmiersprache bot sich an, da sie imperative Merkmale und
darüber hinaus eine relativ einfache Syntax besitzt (vgl. [Gri91, S. 20]). Mit Hilfe ei-
nes für Scheme als Paket angebotenen Abhängigkeitsgraphen entwickelte Griswold einen
Ansatz für ein Werkzeug zur Restrukturierung von Quellcode.
  Die Grundlage für automatisierte Refaktorisierungen objektorientierter Systeme schu-
fen u. a. Ralph E. Johnson und William Opdyke Anfang des Jahres 1990. Sie gingen
davon aus, dass objektorientierte Frameworks nicht von vornherein vollständig geplant
werden könnten und es daher einer Umstrukturierung bedürfte. Sie untersuchten die
Umstrukturierungsmöglichkeiten bezüglich Aggregation und Vererbung5 für die Spra-
che C++. Als Vorteil gegenüber prozedural aufgebauter Software nahmen Johnson und
Opdyke an, dass bei einer notwendigen Erweiterung oder Modifizierung bestehender
Software objektorientiert aufgebaute Systeme geeigneter wären, da hier oft durch ein
Hinzufügen von Klassen die Abhängigkeiten aufgelöst werden könnten. Jedoch würden
trotzdem ebenso Veränderungen objektorientierter Software nötig (vgl. [JO93, S. 1ff.]).
  Opdyke ermittelte aus der Beobachtung von Refaktorisierungen von Programmen Ei-
genschaften, die bei einer automatisierten Refaktorisierung nicht verletzt werden dürfen.
Er befürchtete, dass ein erneutes Übersetzen des Quellcodes nach der Refaktorisierung
zwar die meisten Fehler, jedoch nicht eine Veränderung der Semantik von Referenzen
und Operationen vor und nach der Refaktorisierung entdecke (vgl. [Opd92, S. 39-42]).
  Eines der bekanntesten frühen Werkzeuge, das auf der Grundlage der Arbeiten von
Johnson und Opdyke entwickelt wurde, ist der von John Brant und Donald Bradley
Roberts veröffentlichte Refactoring Browser6 für Smalltalk-Programme. Roberts lieferte
die theoretischen Grundlagen zu dem Browser in einer Dissertation (vgl. [Rob99]). Der
Refactoring Browser nimmt Methodenumbenennungen zur Laufzeit vor und basiert da-
mit auf einem anderen Konzept als die eingangs skizzierte Idee von Refaktorisierungen,
in denen die Quelltexte verändert werden, ohne dass das jeweilige Programm ausge-
 4
   Eine eigentlich funktionale Sprache, in der auch imperativ programmiert werden kann,
    vgl. http://www.cs.hut.fi/Studies/T-93.210/schemetutorial/node9.html, Abruf am 27.01.2012.
 5
   Mit der Einschränkung, dass Mehrfachvererbung und Methodenüberladung ausgeschlossen wurden
    (vgl. [Opd92, S. 35]).
 6
   http://st-www.cs.illinois.edu/users/brant/Refactory/RefactoringBrowser.html

                                             11
führt wird. Da Smalltalk eine dynamische Programmiersprache ist, werden Ideen des
Refactoring Browsers im Folgenden weiter untersucht (s. Abschnitt 5.2).
   Die heute etablierten Werkzeuge zur Unterstützung von Refaktorisierungen sind oft
in Entwicklungsumgebungen integriert oder als Plugin einladbar. Darüber hinaus stehen
für einige Programmiersprachen eigene Programme in Anlehnung an den Refactoring
Browser für Smalltalk zur Verfügung.7
   Bisher sind die Refaktorisierungswerkzeuge sehr eng verknüpft mit der jeweiligen Ent-
wicklungsumgebung (IDE) und Programmiersprache. Für sprachübergreifende Werkzeu-
ge muss jedoch nach einer allgemeinen Basis gesucht werden. Interessant werden hier
exemplarisch Entwicklungsumgebungen wie Eclipse, Netbeans oder RubyMine,8 in de-
nen von Haus aus mehrere Sprachen integriert sind und die deswegen dafür prädestiniert
sind, Refaktorisierungsumgebungen zu vereinheitlichen oder sogar sprachübergreifende
Refaktorisierungswerkzeuge anzubieten.

1.2.2 Refaktorisierungswerkzeuge als eigenes Fachgebiet
Friedrich Steimann wirft im Jahr 2010 in einem Artikel zu korrekten Refaktorisierungen
ein Problem auf: Existierende Refaktorisierungswerkzeuge würden nicht immer korrekt
arbeiten (vgl. [Ste10, S. 24-25]). Er spricht sich, als Lösungsansatz, für den Bau von Re-
faktorisierungswerkzeugen als eigenständige Disziplin aus. Denn verglichen mit dem Bau
von Compilern fehlen ihm anerkannte Ansätze, die identifizierten Refaktorisierungspro-
bleme zu lösen. Er bezieht sich auf in Entwicklungsumgebungen integrierte Werkzeuge,
die trotz hohem Qualitätsstandard dem Praxistest nicht standhalten würden: „Vollstän-
dige und korrekte Spezifikationen von Refaktorisierungen sind also nur im Rahmen der
Entwicklung von Refaktorisierungswerkzeugen sinnvoll. Leider sind auch die heute ver-
fügbaren Refaktorisierungswerkzeuge alles andere als korrekt [...].“ [Ste10, S. 24]
   Zusammengefasst schätzt Steimann existierende Werkzeuge als bisher nicht ausrei-
chend ein und fordert eine Standardisierung, damit auf Dauer die notwendige Qualität
erzielt werden könne (vgl. [Ste10, S. 29]). Daraus folgt die Frage, welche Lösungsansätze
an dieser Stelle als weiterführend betrachtet werden können.
   Steimann bevorzugt einen constraint-basierten Ansatz. Denn der Versuch, die exis-
tierenden Leitfäden und Muster direkt in Refaktorisierungsprogrammen umzusetzen,
beinhaltet für Steimann die Schwierigkeit, sich in zu vielen Fallunterscheidungen zu ver-
lieren. Ebenso distanziert er sich von dem Ansatz, Programme als Graphen darzustellen
und damit Refaktorisierungen als Graphentransformationen durchzuführen (vgl. [Ste10,
S. 27]). Constraints seien dagegen lesbar, da in den Regeln beschrieben wird, was erfüllt
werden muss.
   Steimann sieht für die Zukunft ein Baukastenprinzip: „Anstatt – wie bisher – die
Vorbedingungen und die zur Durchführung notwendigen Schritte für jede Refaktorisie-
rung getrennt zu formulieren und anschließend mit einigem Aufwand in den Kontext
der jeweiligen Entwicklungsumgebung einzubetten, wäre es hilfreich, wenn Refaktorisie-
rungswerkzeuge nach dem Baukastenprinzip aus erprobten Komponenten zusammenge-
 7
     Eine Liste gängiger Refaktorisierungswerkzeuge im Anhang.
 8
     http://www.eclipse.org, http://www.netbeans.org, http://www.jetbrains.com/ruby/

                                              12
stellt werden könnten und wenn sie von den Entwicklungsumgebungen stets die gleiche
Infrastruktur benötigen.“ [Ste10, S. 29]
   Dazu will Steimann ein Framework nutzen, in dem Constraintregeln einheitlich für alle
Sprachen formuliert werden können. Des Weiteren müssen die veränderten Programme
getestet werden können.
   Infolgedessen und als Basis der vorliegenden Arbeit über Refaktorisierungswerkzeuge
für sprachübergreifende Projekte wird die deklarative Sprache Refacola9 (für constraint-
basierte Refaktorisierungen) herangezogen. Refacola ist ein Schritt in die von Steimann
vorgeschlagene Richtung. Refacola soll für verschiedene Programmiersprachen genutzt
werden können; exemplarisch werden momentan Eiffel und Java unterstützt. Sprach-
spezifisch müssen die Programmelemente mit ihren Eigenschaften und Wertebereichen
bestimmt werden, einschließlich der Beziehungen, die sich hieraus ergeben, und den Cons-
traintregeln, die aufgestellt werden müssen (vgl. [SKP11]).
   Ebenso auf Basis von Constraints arbeiten Daniel Speicher, Tobias Rho, Günter
Kniesel an einem Werkzeug für eine logikbasierte Infrastruktur zur Codeanalyse (vgl.
[SRK07]). Das entwickelte Werkzeug JTransformer basiert auf Ausarbeitungen von Tip,
Kiezun und Bäume (vgl. [TKB03]). Tip et al. bearbeiteten das Konzept für Refaktori-
sierungen weiter und integrierten Ideen u. a. in die Eclipse Entwicklungsumgebung (vgl.
[TFKEBS09] und [SDSTT10]).
   Im Hinblick auf die vorliegende Ausarbeitung muss gefragt werden, ob die von Stei-
mann, Speicher et al. und Tip et al. vorgeschlagenen Ansätze auch dann greifen, wenn
mit Hilfe eines Refaktorisierungswerkzeugs sprachübergreifende Projekte refaktorisiert
werden sollen, vor allem mit Blick darauf, dass die Sprachen sich in ihren Konzep-
ten ggf. grundlegend unterscheiden. Dass solche Werkzeuge heute notwendig sind, weil
sprachübergreifende Projekte genutzt werden, soll im Folgenden dargestellt werden.

1.3 Flexibilität durch eine programmiersprachen-übergreifende Entwicklung

Für die vorliegende Ausarbeitung wird die Java Virtual Machine (JVM) als Plattform
für eine programmiersprachenübergreifende Entwicklung herangezogen. Die Java Virtual
Machine und die ursprünglich dafür entwickelte Programmiersprache Java bieten sich im
Hinblick auf sprachübergreifende Refaktorisierungswerkzeuge zum einen deswegen an, da
es für Java bereits Werkzeuge für eine automatisierte Umstrukturierung gibt, auch wenn
diese oft als nicht ausreichend kritisiert werden und zum anderen, da heute viele weitere
Programmiersprachen existieren, die auf dieser Plattform lauffähig sind.
   Ebenso könnte an dieser Stelle jedoch auch das .NET Framework als Basis herange-
zogen werden. Auf der .NET Plattform laufen genauso unterschiedliche Programmier-
sprachen, beispielsweise die zu den objektorientierten Programmiersprachen zählende
Sprache C# oder die funktionale Programmiersprache F# (an OCaml angelehnt). In ei-
nem MSDN-Artikel wird vermerkt: „Auch wenn Programmierer, die über beide Ohren in
der objektorientierten Entwicklung stecken, möglicherweise eine andere Ansicht haben,
sind funktionale Programme häufig für bestimmte Arten von Anwendungen einfacher zu

 9
     http://www.feu.de/ps/prjs/refacola

                                           13
schreiben und zu verwalten.“ [New08]
   Es lohnt sich also, innerhalb eines Projektes zielgerichtet unterschiedliche Program-
miersprachen zu nutzen, nämlich solche, durch die sich die in der Softwareerstellung
jeweils auftretenden Probleme einfacher lösen lassen.
   Das Verflechten mehrerer Programmiersprachen in einem Projekt soll im Weiteren ver-
folgt werden. Im Hinblick auf den eingangs festgelegten Schwerpunkt der Arbeit, dem Zu-
sammenspiel sogenannter dynamischer und statisch typgeprüfter Programmiersprachen,
folgt eine Diskussion des Begriffs dynamisch, eine Skizze der Vorteile der Interaktion
unterschiedlicher Programmiersprachen und ein Überblick über die große Anzahl schon
existierender Sprachen, die auf der JVM laufen. Damit soll die Notwendigkeit der zu-
künftigen Entwicklung programmiersprachenübergreifender Refaktorisierungswerkzeuge
aufgezeigt werden.

1.3.1 Vorteile der Interaktion mit dynamischen Programmiersprachen
Um den Nutzen einer Interaktion statisch typgeprüfter und dynamischer Programmier-
sprachen zu erkunden, muss m. E. der Begriff dynamische Programmiersprache genauer
bestimmt werden, denn es existiert bis heute keine einheitliche Definition dieses Begriffs.
   Kontrovers diskutiert,10 aber dennoch ein eventuell für diese Arbeit aufschlussreicher
Aufsatz wurde im Jahr 2005 von Oscar Nierstrasz, Alexandre Bergel, Marcus Denker,
Stephane Ducasse, Markus Gälli und Roel Wuyts veröffentlicht. Sie behaupten, dass
statische Sprachen von Natur aus nicht geeignet sind für die Realisierung von Anwen-
dungen mit dynamischen Anforderungen: „Inherently static languages will always pose
an obstacle to the effective realization of real applications with essentially dynamic re-
quirements.“ [NBDDGW05, S. 1]
   Sie schließen daraus, dass dringend eine Forschung nach Programmiersprachen not-
wendig ist, die Veränderungen zur Laufzeit eines Programms unterstützen (vgl. ebd.,
S. 2). Denn die Forschung sei eingefahren auf statische Sprachen (vgl. ebd., S. 10).
   Es stellt sich daraus folgend die Frage, wo der Unterschied zwischen dynamischen und
statisch typgeprüften Sprachen liegt.
   Allgemein gilt als grundlegendes Kennzeichen dynamischer Programmiersprachen, dass
solche Sprachen zur Laufzeit Tätigkeiten ausführen, die andere Programmiersprachen
nicht oder zur Übersetzungszeit umsetzen.
   Einerseits werden Programmiersprachen grob eingeordnet nach den Konzepten, die
die jeweilige Sprache implizit anbietet. Wird eine Reihe elementarer (folgend umrisse-
ner) dynamischer Konzepte11 unkompliziert unterstützt, gilt die Sprache als dynamische
Programmiersprache. Unkompliziert deswegen, weil sich eine dynamische Programmier-
sprache nicht unbedingt dadurch abgrenzt, dass eine dagegen stehende, nicht-dynamische
Sprache eine der im Folgenden skizzierten Techniken nicht beherrscht, sondern die Ab-
grenzung erfolgt durch die Natürlichkeit des Einsatzes dieser Technik in der jeweiligen
Programmiersprache. Es ist daher schwer, den Begriff zu fassen.
10
   Vgl. dazu die Diskussion: http://lambda-the-ultimate.org/node/852#comment-7783, Abruf am
    18.01.2012.
11
   Exemplarisch: http://www.lesscode.de/initiative/dynamische-programmiersprachen

                                            14
Als dynamische Konzepte gelten beispielsweise solche, in denen es möglich ist, eine
Variable zur Laufzeit an Werte mit unterschiedlichen Typen zu binden, in denen es
möglich ist, über eine Eingabe direkt Codes zur Ausführung zu bringen („eval“) oder in
denen Reflexion existiert. Reflexion bietet dann weiter die Möglichkeit, Strukturen von
u. a. Klassen oder Funktionen zur Laufzeit zu untersuchen und dynamisch zu nutzen
oder entsprechende Informationen über Objekte zu erhalten.
   Jedoch ist das Aufzählen solcher Konzepte nicht ausreichend, um den Begriff dyna-
mische Programmiersprache klar zu umreißen, da nicht alle als dynamisch geltenden
Sprachen alle Konzepte unterstützen.
   Als einfache Möglichkeit soll deshalb von der konkreten Seite des Nutzens einer Pro-
grammiersprache ausgegangen werden, indem gefragt wird, wo solche Programmierspra-
chen heute typischerweise eingesetzt werden. Damit lassen sich eine Reihe von Program-
miersprachen einordnen nach ihrer Möglichkeit, elegante Lösungen für Probleme zu bie-
ten, die bei statisch getypten Sprachen nur umständlich gelöst werden können. Beispiele
wären Verbindungen zwischen Schnittstellen (Glue Code), Entwicklung von Benutzero-
berflächen, Einsatz von Webservices, schnelles Erstellen von Prototypen, Verwalten von
Projekt-Infrastrukturen mittels Build-Skripten, Entwickeln von Testskripten u. v. m.
   Wenn also in einem einzigen Projekt mehrere Programmiersprachen genutzt werden,
können die Vorteile und Stärken der jeweiligen Sprachen genutzt werden.
   Für die Java Virtual Machine wurden vorwiegend dynamische Programmiersprachen
portiert oder entwickelt, um mit der statisch typgeprüften Sprache Java in Kommuni-
kation zu treten.12

1.3.2 Java Virtual Machine als Basis mehrerer Programmiersprachen
Die weite Verbreitung der Java Virtual Machine und die Abstraktion der darunter lie-
genden Maschinen-Architektur ermöglicht die Entwicklung und den Einsatz von Kompo-
nenten, Frameworks und Services auf unterschiedlichen Betriebssystemen. Jedoch wird
in vielen Gruppen im Umfeld von Softwareentwicklung diskutiert, dass die ursprünglich
für die Plattform entwickelte Programmiersprache Java selbst für bestimmte Einsatzbe-
reiche nicht flexibel genug sei.13
   Darüber hinaus gilt Java verglichen mit Sprachen wie Ruby, Python, Lisp oder Small-
talk als weniger elegant und ausdrucksstark.
   Als eine der ersten neu entwickelten Programmiersprachen versuchte Groovy14 im Jahr
2003, diesen Nachteil zu beheben. Groovy ist dynamisch typisiert und bietet Closures,
Operatorüberladung, eine native Darstellung für BigDecimal und BigInteger u. v. m.
Groovy wird in Bytecode übersetzt und ausgeführt. Java-Klassen können daher Groovy-
Klassen nutzen und umgekehrt
12
   Es existieren jedoch auch Ausnahmen wie die Programmiersprache Scala, die durch ein statisches
    Typsystem nicht in diese Kategorie passt.
13
   Vgl. u. a. http://www.oio.de/public/java/groovy/groovy-einfuehrung.htm,
    http://openbook.galileocomputing.de/javainsel/javainsel_01_002.html,
    http://it-republik.de/jaxenter/artikel/Groovy-fuer-Java-Entwickler-Dynamische-
    Programmierung-auf-der-Java-Plattform-1065.html, alle Abruf am 18.01.2012.
14
   http://groovy.codehaus.org

                                               15
Etwas früher, im Jahr 2000, entstand Jython. Diese Programmiersprache ist nicht
neu entwickelt wie Groovy, sondern eine reine Java-Implementierung der seit Anfang
1990 existierenden Programmiersprache Python15 . Jython ermöglicht die Ausführung
von Python-Programmen auf der Java-Plattform und sämtliche Java-Bibliotheken kön-
nen in Python-Programme importiert und genutzt werden.
   Im Kontext aktueller, auf der Java Virtual Machine lauffähiger Sprachen werden an
dieser Stelle noch die im Jahr 2003 als Forschungssprache entwickelte und als Multipara-
digmen-Sprache bezeichnete Sprache Scala16 und die im Jahr 2007 erschienene Sprache
Clojure17 hervorgehoben. Scala ist im Kern eine objektorientierte Sprache mit statischem
Typsystem, besitzt jedoch darüber hinaus funktionale Möglichkeiten. Clojure ist ein
speziell für die Java Virtual Machine entwickelter Lisp-Dialekt, der insbesondere durch
seine Unterstützung für die Entwicklung von nebenläufigen Anwendungen heraussticht.
   Aus dem existierenden Pool der JVM-fähigen Programmiersprachen wird für diese
Arbeit exemplarisch JRuby18 (eine Implementierung des Ruby-Interpreters in Java) her-
angezogen. JRuby schafft eine Brücke zwischen Ruby und Java und vereint damit die
Vorzüge dieser beiden Sprachen.
   Die Sprache Ruby19 selbst erschien im Jahr 1995. Sie gilt als dynamische Sprache.
Ruby ist rein objektorientiert und wurde darüber hinaus als Multiparadigmen-Sprache
entworfen. Damit steht es dem/der Entwickelnden offen, weitere Programmierparadig-
men zur Erstellung seiner/ihrer Programme zu nutzen (s. Kapitel 3).
   „The Ruby programming language was released to the public in 1995 and gained
widespread adoption in 2006. A multipurpose language that focuses on simplicity and
productivity, it combines the best features of many compiled and interpreted langua-
ges, such as easy development of large programs, rapid prototyping, almost-real-time
development, and compact code. Ruby is a reflective, dynamic, and interpreted object-
oriented scripting language, and JRuby is a Java programming language implementation
of the Ruby language syntax, core libraries, and standard libraries.“ [Paw07]
   Mit JRuby wird, vergleichbar mit den oben genannten Sprachen, die Nutzung der Java
Virtual Machine für die Programmiersprache Ruby ermöglicht. Der Sprachumfang von
Ruby wird von JRuby nahezu vollständig implementiert. JRuby ermöglicht darüber hin-
aus die Interaktion von Java nach Ruby und von Ruby nach Java. JRuby wird momentan
ständig weiterentwickelt und an gegenwärtige Anforderungen angepasst. Im Jahr 2009
wurde eine Version von JRuby vorgestellt, die auf der Software-Plattform für mobile
Geräte, Android, ausführbar ist.20
   Wenn Entwickler und Entwicklerinnen heute für konkrete Problemstellungen nach
einfachen und effizienten Lösungen suchen und sich dafür auf unterschiedliche Spra-
chen mit jeweils unterschiedlichen Sprachkonzepten stützen, stellt sich die Frage, ob
für sprachübergreifende Projekte Refaktorisierungswerkzeuge im Einsatz sind, die den

15
   http://www.python.org
16
   http://www.scala-lang.org
17
   http://clojure.org
18
   http://www.jruby.org
19
   http://www.ruby-lang.org
20
   http://ruboto.org

                                          16
unterschiedlichen Sprachkonzepten gerecht werden.
  Dass sprachübergreifende Refaktorisierungswerkzeuge eine besondere Herausforderung
darstellen, soll durch das Beispiel motiviert werden, dass eine Variable in Ruby keinen
Typ besitzt. „Der Typ von Objekten wird zur Laufzeit überprüft, allerdings sind Varia-
blen typenlos (sozusagen void). Dementsprechend gibt es keine Type-Casts und keine
typedefs.“21 Das macht Ruby als dynamische Sprache flexibel, eröffnet jedoch ein komple-
xes Problemfeld für sprachübergreifende Aufrufe zu einer statisch typgeprüften Sprache
wie Java. Denn es kann durch eine Analyse des Quellcodes nicht entschieden werden,
auf welche Objekte die Ruby-Variable zur Laufzeit referenzieren wird.

1.4 Sprachübergreifende Refaktorisierungswerkzeuge

JRuby als Brücke zwischen Java und Ruby birgt, wie zuvor skizziert, Vorzüge für Projek-
te, in denen für bestimmte Problemstellungen nach einem ausdrucksstarken Code gesucht
wird und trotzdem im vollen Umfang auf existierende Java-Bibliotheken zurückgegriffen
werden soll. Für mehrsprachige Projekte werden dafür nun geeignete sprachübergreifende
Refaktorisierungswerkzeuge benötigt.
   Wenn jedoch schon Refaktorisierungswerkzeuge für einzelne Programmiersprachen
nicht ausreichend untersucht sind und bisher nur unzureichend Unterstützung anbieten,
ist abzusehen, dass solche Werkzeuge für sprachübergreifende Projekte noch am Anfang
stehen. Andreas Thies et al. beschreiben in einem Projekt „Cross-Language Refactoring:
The CLaRe Research Project“22 die Situation folgendermaßen: „In fact, the different sets
of constraint rules required for the different languages mirror commonalities and diffe-
rences of the language specifications in a concise way. It is an open research question
whether cross-language refactorings will require additional constraint rules reflecting the
conditions of accessing program elements across specific language boundaries, or whether
the sharing of constraint variables (including a mapping of their values to the different
domains required by the different languages) suffices. We expect that the number of
constraint rules required for cross-language refactoring will be a linear function of the
numbers of constraint rules required for each individual language.“23
   Für die vorliegende Ausarbeitung soll als Prämisse gelten, dass es eindeutig der falsche
Weg ist, eine der beteiligten Sprachen auf bestimmte, von der/den anderen Sprachen ver-
langte Konstrukte, mit dem Ziel, dass eine Refaktorisierung möglich wird, einzugrenzen.
Dieser Ansatz, eine Sprache wie Ruby auf ausschließlich die Möglichkeiten, die auch Java
bietet, einzuengen, beispielsweise sie mit (wie auch immer gearbeiteten) Typen zu ver-
sehen, wird hier als absolut falsch begriffen. Denn nur die skizzierte Unterschiedlichkeit
der Sprachen ermöglicht einen produktiven, effizienten und ausdrucksvollen Einsatz. Im
Folgenden wird daher versucht, die Unterschiede explizit auszuloten, um den Blick auf
sprachübergreifende Refaktorisierungen zu lenken, die ohne diese Einschränkung ablau-
fen können.
21
   http://www.ruby-lang.org/de/documentation/ruby-from-other-languages/to-ruby-from-c-
    and-c-, Abruf am 10.06.2011.
22
   http://www.fernuni-hagen.de/ps/prjs/CLaRe/
23
   Ebd.

                                            17
Beschränkt wird sich an dieser Stelle auf die Umgebung Eclipse und das im Weite-
ren vorgestellte Dynamic Languages Toolkit (DLTK)24 . Das Eclipse Framework wird
als Open-Source-Umgebung für viele Projekte genutzt. Es unterstützt gängige Program-
miersprachen.25
   Im Folgenden wird als Grundlage eine Möglichkeit der Zusammenarbeit von unter-
schiedlichen Sprachen im Eclipse-Framework beschrieben. Danach wird die Sprache Ru-
by und die Regeln der Interaktion von Ruby und Java vorgestellt. Mit Blick auf einen
constraint-basierten Ansatz wird dann das Framework und die Sprache Refacola vorge-
stellt und exemplarisch eine Regel für einen sprachübergreifenden Methodenaufruf ent-
wickelt. Anhand des Methodenaufrufs wird folgend untersucht, welche Voraussetzungen
für ein sprachübergreifendes Refaktorisieren mit dem constraint-basierten Ansatz gelten
müssen. Die Ausarbeitung schließt mit der Beschreibung der entwickelten Software, in
der die gefundenen Ideen konkret umgesetzt wurden.

24
     http://www.eclipse.org/dltk/
25
     Gewählt wurde die Umgebung nicht zuletzt, weil u. a. das aktuelle Projekt: Ruboto (JRuby für An-
      droid) existiert und Android von dem Eclipse Framework unterstützt wird.

                                                  18
2 Entwicklungsumgebung und mehrsprachige Projekte

Für sprachübergreifende Softwareentwicklung ist es sinnvoll, die unterschiedlichen Pro-
grammiersprachen in einem einzigen Projekt vereinen zu können. Dies ist in der für
diese Arbeit genutzten Entwicklungsumgebung Eclipse aktuell noch nicht möglich, je-
doch in Planung. Im Folgenden wird die Unterstützung von Ruby (hier in der Java-
Implementierung JRuby) in Eclipse skizziert und der Umgang mit den existierenden
Schwierigkeiten der Nutzung zweier unterschiedlicher Projekte (Java und Ruby) aufge-
zeigt.

2.1 Eclipse Dynamic Language Toolkit

Das Eclipse-Projekt bietet die Möglichkeit, über das Dynamic Language Toolkit (DLTK)
komplette Umgebungen für Programmiersprachen zu erstellen. Das Core-Framework von
DLTK stellt dafür sprachunabhängige Bausteine bereit. Entwicklungsumgebungen exis-
tieren bereits für verschiedene dynamische Sprachen, so auch für Ruby.
   Es fehlt jedoch bis heute eine Möglichkeit, mehrere Sprachen in einem einzigen Projekt
gemeinsam zu integrieren. Daher existiert beispielsweise kein übergreifender Debugger.
Im Jahr 2009 sagte Andrey Platov (Projektleitung) in einem Interview: „Mithilfe von
DLTK ist es zwar bereits möglich, Entwicklungstools für neue Sprachen sehr schnell um-
zusetzen. Es fehlt allerdings noch an einer Unterstützung der Interoperabilität zwischen
verschiedenen Programmiersprachen, was insbesondere sehr wichtig für Sprachen ist, die
auf der Java Virtual Machine laufen. Hier gibt es ein großes Bedürfnis an Werkzeugen,
die mit diesen unterschiedlichen Sprachen umgehen können.“1

2.2 Verbindung eines Java- und Ruby-Projekts in der Eclipse

Die Entwicklungsumgebung Eclipse bietet für Java wie für Ruby (über das DLTK) die
Möglichkeit, sprachenspezifische Projekte anzulegen. Ein einziges Projekt für beide Spra-
chen anzulegen, ist bis zu diesem Zeitpunkt jedoch nicht möglich. Eine provisorische Lö-
sung für die Interaktion zwischen den Sprachen ist, zwei Projekte anzulegen und damit
vorerst die existierenden Features (Syntax Highlighting, Debugger, Pretty Printer) für
die jeweilige Programmiersprache zu nutzen (s. Abbildung 2.1). Ein sprachübergreifendes
Verwenden von Werkzeugen ist dann jedoch nicht möglich.

 1
     http://it-republik.de/jaxenter/news/Eclipse-DLTK-Komplette-IDEs-fuer-dynamische-
      Sprachen-bauen-050094.html, Abruf 9.01.2012.

                                             19
Abbildung 2.1: Projekte im Package Explorer

      Bekannt gemacht werden können die Projekte dann über den Klassenpfad, der zum
    Ruby-Skript hinzugefügt wird.

1   require " java "
2   $CLASSPATH
3 Interoperabilität der Programmiersprachen Ruby und Java

Um eine Auseinandersetzung über Refaktorisierungswerkzeuge für eine Software, in der
beide Programmiersprachen, Ruby und Java, genutzt werden, führen zu können, muss
die Fähigkeit der Zusammenarbeit zwischen diesen Sprachen untersucht werden.
  Im Folgenden werden Ruby vorgestellt und elementare für die Zusammenarbeit der
beiden Sprachen wichtige Konvertierungsregeln beschrieben.

3.1 Einordnung der Programmiersprache Ruby

3.1.1 Die Ideale des Ruby-Erfinders
Ruby1 ist eine objektorientierte Programmiersprache, die interpretiert wird.2
   Der Erfinder der Sprache, Yukihiro Matsumoto, begann im Jahr 1993 aus Unzufrieden-
heit über verfügbare Skriptsprachen an einer eigenen Sprache zu arbeiten und gab 1995
die erste Ruby-Version frei. Er nutzte Ideen verschiedener Sprachen (Perl, Smalltalk,
Eiffel, Ada und Lisp)3 und formte daraus eine Programmiersprache, die neben der Ob-
jektorientierung mehrere weitere Programmierparadigmen unterstützt, unter anderem
prozedurale und funktionale Programmierung.
   Ruby bietet darüber hinaus Garbage Collection, Ausnahmen (Exceptions), Reguläre
Ausdrücke, Introspektion, Code-Blöcke als Parameter für Iteratoren und Methoden, die
Erweiterung von Klassen zur Laufzeit, Threads und vieles mehr. Damit ist diese Sprache
für unterschiedlichste Problemstellungen einsetzbar.
   Matsumoto folgte verschiedenen Design-Prinzipien4 . Beispielsweise können Program-
mierende gleiche Probleme mit unterschiedlichen Sprachmitteln lösen, was dem – aus
der Programmiersprache Perl entlehnten – Prinzip: „There is more than one way to do
it“ (TIMTOWTDI) entspricht und Ruby ausdrucksstark machen soll.
   Eine ebenso im Sprachdesign angelegte Flexibilität bietet Matsumoto durch das Kon-
zept „Duck Typing“5 . Ein Objekt kann über Operationen, die es zur Verfügung stellt,
angesprochen werden. In dem Kontext, in dem das Objekt benutzt wird, gilt der Typ
dann als korrekt, wenn das Objekt die erwarteten Operationen anbietet.
   Eines der elementaren Prinzipien, das Matsumoto im Design berücksichtigt hat, ist
das „Principle of least surprise“ (POLS)6 . Im Hinblick auf Programmiersprachen besagt
es, dass (erfahrene) Entwickelnde relativ intuitiv programmieren können sollen, ohne
durch beispielsweise unverständliche Methoden oder Bibliotheksnamen überrascht zu

 1
   Ruby steht für Rubin in Anspielung auf die Programmiersprache Perl (vgl. [SM01]).
 2
   Die JRuby-Implementierung enthält seit 2007 zusätzlich einen Compiler (vgl. [NSEBD11, S. 78-96]).
 3
   Vgl. http://www.ruby-lang.org/en/about/
 4
   Vgl. http://wiki.ruby-portal.de/Rubys_Prinzipien, Abruf am 10.03.2012.
 5
   Vgl. ebd.
 6
   http://www.canonical.org/~kragen/tao-of-programming.html, Abruf am 17.02.2012.

                                                 21
werden. Darüber hinaus wurde Ruby als eine Sprache entworfen, die auch für Nicht-
Programmierende lesbar sein soll. (vgl. [MO08, S. 29])
  Vor allem das Prinzip POLS zeigt, dass der Entwurf der Sprache darauf ausgerichtet
wurde, dass bei der konkreten Programmierung das Problem und nicht die Sprache selbst
im Vordergrund steht.
  Matsumoto entwickelte Ruby mit Emacs7 und gcc8 unter Linux und stellte seine Pro-
grammiersprache (mit Quelltext) als freie Software9 zur Verfügung.
  Ruby ist heute für alle gängigen Betriebssysteme10 verfügbar. Der Ruby-Interpreter
und die Standardbibliothek sind unter den Bedingungen der GNU General Public License
(GPL)11 nutzbar.

3.1.2 Merkmale der Programmiersprache Ruby
Ruby wurde als Multiparadigmen-Sprache entworfen. Das heißt, dass es dem/der Entwi-
ckelnden offen steht, neben der Objektorientierung weitere Programmierparadigmen zur
Erstellung der Programme zu nutzen. Die Umsetzung der verbreitetsten Paradigmen in
Ruby soll im Folgenden beschrieben werden.

Objektorientierte Programmierung
In Anlehnung an Smalltalk ist Ruby rein objektorientiert. Matsumoto verfolgte im Ruby-
Sprachdesign die Zielsetzung: Everything is an Object (EIAO)12 . Beispielsweise sind
auch die – in vielen anderen Sprachen als primitive Typen geltenden – Zahlen und
Zeichenketten Objekte (s. Listing 3.1 und 3.2).

p u t s 3 . 1 4 1 5 . c l a s s # Ausgabe => float

                                    Listing 3.1: Zahlen sind Objekte

’A B C ’ . s p l i t ( ’ ’ ) # Ergebnis = >[ ’A ’, ’B ’, ’C ’]

                                Listing 3.2: Zeichenketten sind Objekte

Ruby unterstützt verschiedene Ansätze der Objektorientierung:

• Klassenbasierte Objektorientierung:
     Eine Klasse dient als Vorlage. Von ihr werden Instanzen erstellt. Klassen können von
     anderen abgeleitet werden.

 7
   http://www.gnu.org/software/emacs/
 8
   GNU Compiler Collection (http://gcc.gnu.org/).
 9
   http://www.ruby-lang.org/de/about/license.txt, Abruf am 15.02.2012.
10
   Alle Windows-Versionen ab Windows 95 und NT 4.0, MS-Dos, Mac OS und Mac OS X, IMB OS/2,
    alle Linux Distributionen, FreeBSD, OpenBSD, NetBSD und Sun Solaris u. v. m.
11
   http://www.gnu.org/licenses/gpl-3.0.html, Abruf am 14.02.2012.
12
   http://wiki.ruby-portal.de/EIAO, Abruf am 16.02.2012.

                                                     22
Sie können auch lesen