Typenschlüssel durch Unterklassen ersetzen

Một phần của tài liệu AW refactoring improving the design of existing code (Trang 251 - 255)

Sie haben einen unverọnderbaren Typenschlỹssel, der das Verhalten einer Klasse beeinflusst.

Ersetzen Sie den Typenschlüssel durch Unterklassen.

8.14.1 Motivation

Wenn Sie einen Typenschlüssel haben, der das Verhalten nicht beeinflusst, so kửnnen Sie Typenschlỹssel durch Klasse ersetzen (221) anwenden. Beeinflusst der Typenschlỹssel aber das Verhalten, so ist das Beste, was Sie tun kửnnen, diese Ver- haltensvarianten durch Polymorphismus zu behandeln.

Diese Situation zeigt sich meist durch die Verzweigungen auf Grund von Bedin- gungen. Dies kửnnen switch-Befehle oder if-then-else-Befehle sein. In jedem Fall prỹfen sie den Wert des Typenschlỹssels und fỹhren dann in Abhọngigkeit von diesem Wert verschiedenen Code aus. Solche Bedingungen müssen durch Be- dingung durch Polymorphismus ersetzen (259) refaktorisiert werden. Damit diese Re- faktorisierung funktioniert, muss der Typenschlüssel durch eine Vererbungsstruk- tur ersetzt werden, die das polymorphe Verhalten beherbergen wird. Eine solche Vererbungsstruktur hat eine Oberklasse und Unterklassen für jeden Typenschlüs- sel.

ENGINEER : int SALESMAN : int type : int

Employee

Employee

Engineer Salesman

Der einfachste Weg, diese Struktur zu erstellen, ist Typenschlüssel durch Unterklas- sen ersetzen. Sie nehmen die Klasse mit dem Typenschlüssel und erstellen eine Un- terklasse fỹr jeden Typenschlỹssel. Es gibt aber Fọlle, in denen Sie dies nicht tun kửnnen. In dem einen Fall ọndert sich der Typenschlỹssel eines Objekts, nach- dem es erstellt wurde. Im anderen wurden bereits Unterklassen der Klasse mit dem Typenschlỹssel gebildet. In beiden Fọllen mỹssen Sie Typenschlỹssel durch Zu- stand/Strategie ersetzen (231) anwenden.

Typenschlüssel durch Unterklassen ersetzen liefert vor allem ein Gerüst, das die Ver- wendung von Bedingten Ausdruck durch Polymorphismus ersetzen (259) ermửglicht.

Der Auslửser, Typenschlỹssel durch Unterklassen zu ersetzen (227) anzuwenden, ist die Anwesenheit bedingter Befehle. Gibt es keine bedingten Befehle, ist Typen- schlüssel durch Klasse ersetzen (221) die weniger kritische Änderung.

Ein anderer Grund dafür, Typenschlüssel durch Unterklassen ersetzen (227) anzu- wenden, sind Elemente, die nur für Objekte mit bestimmten Typenschlüsseln re- levant sind. Nachdem Sie diese Refaktorisierung durchgefỹhrt haben, kửnnen Sie Methode nach unten verschieben (331) und Feld nach unten verschieben (339) einset- zen, um klarzustellen, dass diese Elemente nur in bestimmten Fọllen relevant sind.

Der Vorteil von Typenschlüssel durch Unterklassen ersetzen (227) ist, dass so das Wis- sen über Verhaltensvarianten von den Clients in die Klasse wandert. Wenn ich neue Varianten hinzufüge, muss ich nur eine weitere Unterklasse hinzufügen.

Ohne Polymorphismus mỹsste ich alle Bedingungen suchen und diese ọndern.

Diese Refaktorisierung ist also besonders nützlich, wenn sich die Varianten lau- fend ọndern.

8.14.2 Vorgehen

• Kapseln Sie den Typenschlüssel als eigenes Feld (Eigenes Feld kapseln (171)).

Wenn der Typenschlüssel dem Konstruktor übergeben wird, müssen Sie Konstruk- tor durch Fabrikmethode ersetzen (313) verwenden.

• Erstellen Sie fỹr jeden Wert des Typenschlỹssels eine Unterklasse. ĩberschrei- ben Sie jeweils die get-Methode für den Typenschlüssel, um den relevanten Wert zu liefern.

Dieser Wert wird direkt angegeben (z.B. return 1). Das sieht schlimm aus, ist aber nur eine temporọre Maònahme, bis alle switch-Befehle entfernt worden sind.

• Wandeln Sie nach jeder Ersetzung eines Typenschlüsselwerts durch eine Un- terklasse um und testen Sie.

• Entfernen Sie das Typenschlüsselfeld aus der Oberklasse. Deklarieren Sie die Zugriffsmethoden für den Typenschlüssel als abstrakt.

• Wandeln Sie um und testen Sie.

8.14.3 Beispiel

Ich verwende das langweilige und unrealistische Beispiel von Gehaltszahlungen an Mitarbeiter (Employee):

class Employee...

private int _type;

static final int ENGINEER = 0;

static final int SALESMAN = 1;

static final int MANAGER = 2;

Employee (int type) { _type = type;

}

Der erste Schritt besteht darin, Eigenes Feld kapseln (171) auf den Typenschlüssel _type anzuwenden.

int getType() { return _type;

}

Da der Konstruktor von Employee den Typenschlüssel als Parameter verwendet, muss ich ihn durch eine Fabrikmethode ersetzen:

Employee create(int type) { return new Employee(type);

}

private Employee (int type) { _type = type;

}

Nun kann ich mit Engineer als einer Unterklasse beginnen.

class Engineer extends Employee { int getType() {

return Employee.ENGINEER;

} }

Ich muss auch die Fabrikmethode anpassen, um das entsprechende Objekt zu lie- fern:

class Employee

static Employee create(int type) {

if (type == ENGINEER) return new Engineer();

else return new Employee(type);

}

So mache ich Stück für Stück weiter, bis alle Typenschlüssel durch Unterklassen ersetzt sind. An dieser Stelle kann ich mich des Feldes _type in der Klasse Employee entledigen und getType zu einer abstrakten Methode machen. Nun sieht die Fab- rikmethode so aus:

abstract int getType();

static Employee create(int type) { switch (type) {

case ENGINEER:

return new Engineer();

case SALESMAN:

return new Salesman();

case MANAGER:

return new Manager();

default:

throw new IllegalArgumentException("Incorrect type code value");

} }

Natürlich ist das ein switch-Befehl, den ich lieber vermeiden würde. Aber es ist nur einer und er wird nur bei der Erstellung von Objekten verwendet.

Nachdem Sie die Unterklassen erstellt haben, sollten Sie natürlich Methode nach unten verschieben (337) und Feld nach unten verschieben (339) auf alle Methoden und Felder anwenden, die nur für eine bestimmte Unterklasse von Employee rele- vant sind.

Một phần của tài liệu AW refactoring improving the design of existing code (Trang 251 - 255)

Tải bản đầy đủ (PDF)

(468 trang)