Refactoring

OO\FO

Im Code

In der GUI

Bemerkungen

Create Base

OO

x


Erstellt eine Basisklasse in einem vorgegebenen Layer. Das vorgegebenene Layer muss bereits existieren und der Klassenname darf in dem Layer noch nicht vorkommen. (Bei der Implementierung im Typsystem-Code muss die Datei nicht unbedingt im selben Ordner landen wie die anderen Klassen des Layers. Bei der GUI-Umsetzung wird darauf geachtet das alle Klassen im selben Ordner sind.)

Benötigte Refactorings: -
Ermittelte Konflikte: -

Create Refinement

FO

x


Erstellt eine Refinementklasse in einem vorgegebenen Layer. Wenn das vorgegebenene Layer noch nicht existiert, wird es erstellt. Der Klassenname darf in dem Layer noch nicht vorkommen. Die Basisklasse zum Refinement muss angegeben werden und muss existieren. (Bei der Implementierung im Typsystem-Code muss die Datei nicht unbedingt im selben Ordner landen wie die anderen Klassen des Layers. Bei der GUI-Umsetzung wird darauf geachtet das alle Klassen im selben Ordner sind.)

Benötigte Refactorings: Create Base
Ermittelte Konflikte: -

Create Method

OO

x


Erstellt eine Methode in einer vorgegebenen Klasse, die bereits existieren muss. (Beim Erstellen wird noch nicht geprüft ob in der Klasse bereits eine Methode mit dem Namen und den selben Parametern vorhanden ist. Das geschieht erst, wenn bei einem Refactoring auf diese Methode zugegriffen wird.)

Benötigte Refactorings: -
Ermittelte Konflikte: -

Change Modifier

OO\FO

x

x

Ändert die Modifier von Klassen, Methoden und Attributen. Dieses Refactoring ist noch nicht komplett implementiert. Zur Zeit lassen sich nur Sichtbarkeits-Modifier (private, protected, public, packageprivate) ändern. Bei Klassen wird darauf geachtet, dass diese nur public oder packageprivate sein können. Die Änderungen können wahlweise nur in der aktuellen Klasse oder in allen Refinements einschließlich der Basisklasse durchgeführt werden.

Benötigte Refactorings: -
Ermittelte Konflikte: falls Sichtbarkeit eingeschränkt wird, werden Methodenaufrufe und Feldzugriffe aus anderen Klassen (die nicht mehr möglich sind), sowie Abstrakte Methoden (die nicht mehr erreichbar sind), ermittelt

Move Refinement

FO

x

x

Extrahiert eine Refinementklasse in ein anderes Layer. Wenn das Layer noch nicht existiert, wird es angelegt. Es wird nicht geprüft, ob im Layer schon eine Klasse mit dem selben Namen existiert.

Benötigte Refactorings: -
Ermittelte Konflikte: das extrahierte Refinement darf keine neuen Methoden definieren

Move Refining Method

FO

x

x

Extrahiert eine Refinementmethode in eine andere Refinementklasse. Wenn das Ziellayer oder die Refinementklasse noch nicht existieren, werden sie angelegt. Das Extrahieren von Konstruktoren wird noch nicht unterstützt. Methoden mit dem selben Namen und den selben Parametern dürfen noch nicht in der Zielklasse vorhanden sein.

Benötigte Refactorings: Create Base, Create Refinement
Ermittelte Konflikte: Zugriffe auf private Objektattribute innerhalb der extrahierten Methode; Zugriffe auf private Methoden innerhalb der extrahierten Methode

Extract Method Statements

FO

x

x

Extrahiert Codefragmente aus einer Methode in eine Refinementklasse. Die Methode darf im Ziel-Refinement noch nicht existieren. Die gewählten Codefragmente müssen sich alle im selben Scope befinden (und die Anfangszeile des Codebereichs muss größergleich der Endzeile sein). Da das Refactoring auf der Basis von Zeilennummern geschieht, müssen alle neuen Statements in einer neuen Zeile erfolgen, d.h. folgendes Statement könnte nicht extrahiert werden:
public void Methode1() { Methode2(); }
Wenn das Ziellayer bereits existiert, muss es nach dem Layer der Quelldatei definiert wurden sein, wenn es noch nicht existiert, wird es angelegt. Je nach Situation werden die Statements einfach in die Methode des neuen Refinements, in eine Hook-Methode oder in ein Methodenobjekt extrahiert. Falls die Extraktion in eine Hook-Methode erfolgt, kann der Name dieser Methode festgelegt werden.

Benötigte Refactorings: Create Base, Create Refinement, Create Method
Ermittelte Konflikte: Zugriffe auf private Objektattribute innerhalb der extrahierten Statements; Zugriffe auf private Methoden innerhalb der extrahierten Statements; Definitionen von temp. Variablen innerhalb der zu extrahierenden Statements, die von nachfolgenden Statements referenziert werden; Return-Statements innerhalb der zu extrahierenden Statements bei Extraktion in eine Hook-Methode oder in ein Methodenobjekt bei dem zusätzlich eine Hook-Methode benötigt wird; Return-Statements am Ende der zu extrahierenden Statements



Festlegen der Strategie beim Extrahieren von Statements aus einer Methode:

  1. Ermitteln der lokalen Variablen, die im zu extrahierenden Bereich geändert und hinter diesem referenziert werden.
  2. Wenn mehr als eine solche Variable existiert wird ein Methoden-Objekt angelegt.
  3. Sonst wird geprüft ob eine Hookmethode benötigt wird. Das ist der Fall, wenn eine der folgenden Bedingungen gilt:
    1. Das Statement wird von anderen Statements umschlossen.
    2. Referenzen zu temporären Variablen existieren, die in vorherigen Statements deklariert wurden. (Diese Variablen werden dann als Methodenparameter an die Hook-Methode übergeben.).
    3. Genau eine lokale Variable wird geändert, die hinterher referenziert wird (Diese Variable kann dann als Rückgabewert der Hook-Methode verwendet werden.).
    4. Die Statements referenzieren vorher geänderte lokale Variablen. (Diese Variablen werden dann als Methodenparameter an die Hook-Methode übergeben.).
  4. Wenn weder ein Methoden-Objekt noch eine Hook-Methode angelegt werden muss, werden die Statements einfach in die Methode des Ziel-Refinements verschoben.


Vorgehen beim Anlegen eines Methoden-Objekts:

  1. Anlegen des Methodenobjekts im Layer der Ausgangsklasse. Name der so entstehenden Klasse: Methodenname+MethodObj.jak.
  2. Im Methodenobjekt wird die Ausgangsklasse als Objektattribut mit dem Modifier final und dem Namen SOURCE angelegt.
  3. Im Methodenobjekt werden die Methodenparameter der Ausgangsmethode und alle in der Ausgangsmethode deklarierten lokalen Variablen als Objektattribute angelegt (unabhängig davon, ob später auch wirklich alle benötigt werden).
  4. Im Methodenobjekt wird ein Konstruktor angelegt mit der Ausgangsklasse (Name = SOURCE) und allen Parametern (Namen bleiben erhalten) der Methode, aus der die Statements extrahiert werden sollen, als Konstruktorparameter. Dabei werden auch die Statements "this.Parameter = Konstruktorparameter;" innerhalb des Konstruktors erzeugt.
  5. Die Methode compute() wird ins Methodenobjekt eingefügt. Der Rückgabetyp der Methode ist der selbe, wie der der Ausgangsmethode.
  6. Der gesamte Methodeninhalt der Ausgangsmethode wird in compute() verschoben.
  7. In der Ausgangsmethode wird eine Instanz des Methodenobjekts angelegt und ein Aufruf von compute() erzeugt. Wenn der Rückgabetyp der Ausgangsmethode (und damit der Rückgabetyp von compute()) nicht void ist, wird zusätzlich das Schlüsselwort return vorangestellt.
  8. Bei allen Attributreferenzen in der Methode compute(), die sich auf Objektattribute der Ausgangsklasse beziehen, wird als Scope "SOURCE." davor gesetzt (d.h. z.B. aus ObjektAttributA = 5; wird SOURCE.ObjektAttributA = 5;).
  9. Bei allen Methodenaufrufen in der Methode compute(), die sich auf Methoden der Ausgangsklasse beziehen, wird als Scope "SOURCE." davor gesetzt (d.h. z.B. aus MethodeInAusgangsklasseA(); wird SOURCE.MethodeInAusgangsklasseA();).
  10. Allen Variablendeklarationen in der Methode compute() die gleichzeitig initialisiert werden, werden in Variablenzuweisungen geändert (d.h. z.B. aus int VariableA = 5; wird VariableA = 5;).
  11. Aus der compute()-Methode werden die Statements jetzt in ein Refinement des Methodenobjekts im gewünschten Layer extrahiert (je nach Situation einfach in die Methode des Ziel-Refinements oder in eine Hook-Methode).


Vorgehen beim Anlegen einer Hook-Methode:

  1. Wenn das Refinement, in das extrahiert werden soll, noch nicht existiert, wird es angelegt.
  2. Die Hook-Methode wird in der Ausgangsklasse sowie in der Zielklasse angelegt. Der Name kann selbst gewählt werden. Wenn das nicht geschieht ist der Name der Hook-Methode hook() (wenn möglich) oder hooki(), wobei i die erste positive Zahl ist, bei der der Name noch nicht im Ziel-Refinement vorkommt. Rückgabewert ist entweder void oder der Typ der Variable, die innerhalb der Statements geändert und hinterher referenziert wird. Die Methode in der Zielklasse erhält den Modifier OVERRIDES.
  3. Die Statements aus der Ausgangsmethode werden in die Hookmethode der Zielklasse extrahiert.
  4. Falls erforderlich (Rückgabewert ist nicht void) wird ans Ende der beiden Hookmethoden ein return-Statement erstellt.
  5. In der Ausgangsmethode wird dort, wo vorher die zu extrahierenden Statements waren ein Aufruf zu der Hookmethode eingefügt (ggf. mit der Zuweisung der Variable die geändert wurde und hinterher referenziert wird : z.B. VariableA = hook();).


Vorgehen beim Extrahieren in die Methode des Ziel-Refinements:

  1. Wenn das Refinement, in das extrahiert werden soll, noch nicht existiert, wird es angelegt.
  2. Im Ziel-Refinement wird dieselbe Methode wie die Ausgangsmethode angelegt.
  3. Die Statements aus der Ausgangsmethode werden in die Methode des Ziel-Refinements extrahiert.
  4. Ein Super() Aufruf wird am Anfang oder am Ende der erstellten Methode erzeugt.