Sie haben eine Klasse mit vielen gleichen Instanzen, die Sie durch ein einzelnes Objekt ersetzen wollen.
Verwandeln Sie das Objekt in ein Referenzobjekt.
8.3.1 Motivation
Sie kửnnen eine Klassifikation von Objekten vornehmen, die in vielen Systemen nützlich ist: Referenzobjekte und Wertobjekte. Referenzobjekte sind Dinge wie ein Kunde oder ein Konto. Jedes Objekt reprọsentiert ein Objekt im wirklichen Leben, und Sie verwenden die Objektidentitọt, um festzustellen, ob sie gleich sind. Wert- objekte sind Dinge wie ein Datum oder Geld. Sie sind ausschlieòlich durch ihre Werte definiert. Es interessiert Sie nicht, ob Kopien existieren; Sie kửnnen Hun- derte von ằ1.1.2000ô-Objekte in Ihrem System haben. Sie mỹssen aber sagen kửn- nen, ob zwei von diesen Objekten gleich sind, also müssen Sie die Methode equal (und auch gleich die Methode hashCode) überschreiben.
Die Unterscheidung zwischen Wert und Referenz ist nicht immer offensichtlich.
Manchmal beginnen Sie mit einem einfachen Wert und einer kleinen Menge un- verọnderlicher Daten. Dann wollen Sie ihm einige verọnderliche Daten geben und sicherstellen, dass die Änderungen jeden erreichen, der das Objekt referen- ziert. Zu diesem Zeitpunkt müssen Sie es in ein Referenzobjekt verwandeln.
name: String Customer
Order 1
name: String Customer
Order 1
➾
∗
8.3.2 Vorgehen
• Verwenden Sie Konstruktor durch Fabrikmethode ersetzen (313).
• Wandeln Sie um und testen Sie.
• Entscheiden Sie, welches Objekt dafür verantwortlich ist, den Zugriff auf die Objekte zu gewọhren.
Das kann ein statisches Dictionary sein oder ein Objekt, das den Zugriff steuert.
Sie kửnnen mehr als ein Objekt haben, das als Zugriffspunkt fỹr das neue Objekt dient.
• Entscheiden Sie, ob die Objekte vorab oder nach Bedarf erzeugt werden.
Wenn die Objekte vorab erzeugt werden und Sie sie aus dem Speicher holen, so mỹssen Sie sicherstellen, dass sie geladen werden, bevor sie benửtigt werden.
• Ändern Sie die Fabrikmethode so, dass ein Referenzobjekt zurückgegeben wird.
Wenn die Objekte vorab erstellt werden, so müssen Sie entscheiden, wie Fehler ge- handhabt werden, wenn jemand ein Objekt anfordert, das nicht existiert.
Sie kửnnen gegebenenfalls Methode umbenennen (279) auf die Fabrikmethode an- wenden, um klar zum Ausdruck zu bringen, dass sie ein existierendes Objekt zu- rückgibt.
• Wandeln Sie um und testen Sie.
8.3.3 Beispiel
Ich beginne dort, wo ich in dem Beispiel zu Wert durch Objekt ersetzen (175) aufge- hửrt habe.
Ich habe die folgende Klasse Customer: class Customer {
public Customer (String name) { _name = name;
}
public String getName() { return _name;
}
private final String _name;
}
➾
➾
➾
➾
➾
Sie wird von einer Klasse Order verwendet:
class Order...
public Order (String customerName) { _customer = new Customer(customerName);
}
public void setCustomer(String customerName) { _customer = new Customer(customerName);
}
public String getCustomerName() { return _customer.getName();
}
private Customer _customer;
und von folgendem Clientcode:
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;
}
Zu diesem Zeitpunkt handelt es sich um einen Wert. Jeder Auftrag (Order) hat sein eigenes Kundenobjekt (der Klasse Customer), selbst wenn es sich um denselben Kunden handelt. Ich mửchte dies so ọndern, dass verschiedene Auftrọge mit dem- selben Kunden sich ein Objekt der Klasse Customer teilen.
Ich beginne, indem ich Konstruktor durch Fabrikmethode ersetzen (313) anwende.
Dies gibt mir die Kontrolle ỹber den Erzeugungsprozess, die spọter wichtig wird.
Ich definiere die Fabrikmethode in der Klasse Customer:
class Customer {
public static Customer create (String name) { return new Customer(name);
}
Dann ersetze ich die Aufrufe des Konstruktors durch Aufrufe der Fabrikmethode:
class Order {
public Order (String customer) {
_customer = Customer.create(customer);
}
Anschlieòend mache ich den Konstruktor privat:
class Customer {
private Customer (String name) { _name = name;
}
Nun muss ich entscheiden, wie ich auf meine Customer-Objekte zugreife. Ich be- vorzuge es, ein anderes Objekt zu verwenden. Das funktioniert gut mit so etwas wie den Positionen eines Auftrags. Der Auftrag ist verantwortlich dafür, Zugriff auf seine Positionen zu gewọhren. In dieser Situation erstelle ich meist ein Kon- trollobjekt, das als Zugriffspunkt dient. Der Einfachheit halber speichere ich es hier aber in einem static-Feld der Klasse Customer, so dass Customer der Zugriffs- punkt ist:
private static Dictionary _instances = new Hashtable();
Dann muss ich entscheiden, ob Objekte der Klasse Customer nach Bedarf erzeugt werden sollen oder vorab. Ich entscheide mich für letzteres. Wenn meine Anwen- dung geladen wird, lade ich die benutzten Kunden. Sie kửnnen aus einer Daten- bank oder einer Datei kommen. Der Einfachheit halber verwende ich expliziten Code. Ich kann spọter immer noch Algorithmus ersetzen (136) verwenden:
class Customer...
static void loadCustomers() {
new Customer ("Lemon Car Hire").store();
new Customer ("Associated Coffee Machines").store();
new Customer ("Bilston Gasworks").store();
}
private void store() {
_instances.put(this.getName(), this);
}
Nun verọndere ich die Fabrikmethode, um einen vorfabrizierten Kunden zu lie- fern:
public static Customer create (String name) { return (Customer) _instances.get(name);
}
Da die Fabrikmethode immer einen existierenden Kunden liefert, sollte ich dies auch im Namen zum Ausdruck bringen, indem ich Methode umbenennen (279) ein- setze:
class Customer...
public static Customer getNamed (String name) { return (Customer) _instances.get(name);
}