Sie haben ein Datenelement, das zusọtzliche Daten oder zusọtzliches Verhalten benửtigt.
Verwandeln Sie das Datenelement in eine Klasse.
8.2.1 Motivation
Oft treffen Sie in frühen Stufen der Entwicklung Entscheidungen über die Darstel- lung einfacher Tatsachen als einfache Datenelemente. Wọhrend der Entwicklung erkennen Sie, dass diese einfachen Datenelemente gar nicht mehr so einfach sind.
Eine Telefonnummer kann eine Zeit lang als String dargestellt werden, aber spọter erkennen Sie, dass das Telefon spezielles Verhalten erfordert, um die Ausgabe zu formatieren, die Ortsnetzkennzahl zu extrahieren usw. Für eines oder zwei dieser Dinge kửnnen Sie die Methoden in dem besitzenden Objekt realisieren, aber schnell beginnt der Code nach Redundanz und Neid zu riechen. Wenn Sie diesen Geruch wahrnehmen, machen Sie aus dem Datenelement ein Objekt.
8.2.2 Vorgehen
• Erstellen Sie eine Klasse für den Wert. Geben Sie ihr ein finales Feld vom glei- chen Typ wie der Wert in der Ausgangsklasse. Fügen Sie eine get-Methode und einen Konstruktor hinzu, die das Feld als Argument erhalten.
• Wandeln Sie um.
• Ändern Sie den Typ des Felds in der Ausgangsklasse in die neue Klasse.
customer: String Order
name: String Customer
Order 1
➾
• Lassen Sie die get-Methode in der Ausgangsklasse die get-Methode der neuen Klasse aufrufen.
• Wenn das Feld im Konstruktor der Ausgangsklasse benutzt wird, weisen Sie das Feld mittels des Konstruktors der neuen Klasse zu.
• Ändern Sie die get-Methode so, dass sie eine neue Instanz der neuen Klasse lie- fert.
• Wandeln Sie um und testen Sie.
• Es kann sein, dass Sie nun Wert durch Referenz ersetzen (179) auf das neue Objekt anwenden müssen.
8.2.3 Beispiel
Ich beginne mit einer Klasse Auftrag (Order), die den Kunden (_customer) als String speichert, und mửchte aus dem Kunden eine Klasse Customer machen. Auf diese Weise habe ich etwas, wo ich Daten wie eine Adresse oder eine Bonitọt und nützliches Verhalten, das diese Informationen nutzt, speichern kann.
class Order...
public Order (String customer) { _customer = customer;
}
public String getCustomer() { return _customer;
}
public void setCustomer(String arg) { _customer = arg;
}
private String _customer;
Der Code eines Clients, der diese Klasse benutzt, sieht so aus:
private static int numberOfOrdersFor(Collection orders, String customer) { int result = 0;
Iterator iter = orders.iterator();
while (iter.hasNext()) {
Order each = (Order) iter.next();
if (each.getCustomerName().equals(customer)) result++;
}
return result;
}
Zuerst erstelle ich die neue Klasse Customer. Ich gebe ihr ein finales Feld vom Typ String als Attribut, denn das ist es, was Order jetzt verwendet. Ich nenne es _name, denn ein Name scheint es zu sein, wofür Order es im Augenblick verwendet. Ich füge auch eine get-Methode und einen Konstruktor ein, der dies Feld als einen Pa- rameter nimmt:
class Customer {
public Customer (String name) { _name = name;
}
public String getName() { return _name;
}
private final String _name;
}
Nun ọndere ich den Typ des Kundenfelds (_name) und ọndere die Methoden, die es benutzen, so dass sie die geeigneten Referenzen auf die Klasse Customer verwen- den. Die get-Methode und der Konstruktor liegen auf der Hand. Für die set-Me- thode erzeuge ich einen neuen Kunden:
class Order...
public Order (String customer) { _customer = new Customer(customer);
}
public String getCustomer() { return _customer.getName();
}
private Customer _customer;
public void setCustomer(String arg) { _customer = new Customer(customer);
}
Die set-Methode erstellt ein neues Objekt der Klasse Customer, denn das alte String-Attribut war ebenfalls ein Wert, also ist der Kunde zur Zeit auch ein Wert- objekt. Das heiòt, jeder Auftrag hat sein eigenes Kundenobjekt. Nach einer be- wọhrten Regel sollten Wertobjekte unverọnderlich sein, um einige unangenehme Aliasing-Fehler zu vermeiden. Spọter werde ich das Wertobjekt Customer durch ein Referenzobjekt ersetzen wollen, aber das ist eine weitere Refaktorisierung. Zu die- sem Zeitpunkt kann ich umwandlen und testen.
Nun untersuche ich die Methoden von Order, die Customer verọndern, und nehme einige Änderungen vor, um den jetzigen Stand der Dinge klarer herauszuarbeiten.
Für die get-Methode verwende ich Methode umbenennen (279), um klar zu machen, dass es der Name und nicht das Objekt ist, was zurückgeliefert wird:
public String getCustomerName() { return _customer.getName();
}
Beim Konstruktor und der set-Methode brauche ich die Signatur nicht zu ọndern, aber den Namen des Arguments kann ich aussagekrọftiger gestalten:
public Order (String customerName) { _customer = new Customer(customerName);
}
public void setCustomer(String customerName) { _customer = new Customer(customerName);
}
Weitere Refaktorisierungen kửnnen sehr wohl dazu fỹhren, dass ich einen neuen Konstruktor und eine set-Methode ergọnze, die ein vorhandenes Objekt der Klasse Customer als Parameter erhalten.
Damit endet diese Refaktorisierung, aber in diesem Fall, wie in vielen anderen, folgen weitere Schritte. Wenn ich unserer Klasse Customer Dinge wie eine Bonitọt und Adressen hinzufỹgen mửchte, kann ich dies so noch nicht machen. Das liegt daran, dass der Kunde als ein Wertobjekt behandelt wird. Jeder Auftrag hat sein eigenes Kundenobjekt. Um einem Kunden diese Attribute zu geben, muss ich Wert durch Referenz ersetzen (179) auf die Klasse Customer anwenden, so dass alle Auftrọge fỹr denselben Kunden ein gemeinsames Objekt der Klasse Customer nut- zen. Dort wird dieses Beispiel fortgesetzt.