Ausnahme durch Bedingung ersetzen

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

Sie lửsen eine Ausnahme unter einer Bedingung aus, die der Aufrufer zuvor ge- prüft haben sollte.

Lassen Sie den Aufrufer erst den Test durchführen.

10.15.1 Motivation

Ausnahmen stellen einen wichtigen Fortschritt in den Programmiersprachen dar.

Sie ermửglichen es uns, durch Fehlercode durch Ausnahme ersetzen (319) komplexe Codes zu vermeiden. Wie viele Vergnỹgungen kửnnen aber auch Ausnahmen im ĩbermaò eingesetzt werden, und dann sind sie nicht mehr erfreulich. (Sogar ich kann zu viel von Aventinus1 bekommen [Jackson].) Ausnahmen sollten für Aus- nahmen verwendet werden – Verhalten, das einen unerwarteten Fehler darstellt.

Sie sollten nicht als Ersatz für Bedingungen dienen. Wenn Sie sinnvollerweise er- warten kửnnen, dass der Aufrufer die Bedingung vor dem Aufruf prỹft, so sollten Sie einen Test zur Verfügung stellen, und der Aufrufer sollte ihn verwenden.

double getValueForPeriod (int periodNumber) { try {

return _values[periodNumber];

} catch (ArrayIndexOutOfBoundsException e) { return 0;

} }

double getValueForPeriod (int periodNumber) { if (periodNumber >= _values.length) return 0;

return _values[periodNumber];

}

1. Anm. d. ĩ.: Ein Weizenstarkbier mit 18% Stammwỹrze und 8% Alkoholgehalt.

10.15.2 Vorgehen

• Erstellen Sie als Erstes einen if-then-else-Block, und kopieren Sie den Code aus dem catch-Block in den geeigneten Zweig des if-Befehls.

• Fügen Sie in den catch-Block eine Zusicherung ein, die Sie benachrichtigt, wenn der catch-Block ausgeführt wird.

• Wandeln Sie um und testen Sie.

• Entfernen Sie den catch-Block und den try-Block, falls es keine weiteren catch- Blửcke gibt.

• Wandeln Sie um und testen Sie.

10.15.3 Beispiel

In diesem Beispiel verwende ich ein Objekt, das Ressourcen verwaltet, die aufwen- dig zu erstellen sind, aber wiederverwendet werden kửnnen. Ein gutes Beispiel hierfür sind Datenbankverbindungen. Ein solches Objekt hat zwei Pools von Res- sourcen: einen, der verfügbar ist, und einen, der benutzt wird. Wenn ein Client eine Ressource benửtigt, gibt der Pool sie aus und schiebt sie aus dem verfỹgbaren Pool in den benutzten. Wenn ein Client eine Ressource freigibt, gibt das Objekt sie wieder zurück. Wenn ein Client eine Ressource anfordert und keine frei ist, er- zeugt das Objekt eine neue.

Die Methode zum Ausgeben der Ressourcen kửnnte so aussehen:

class ResourcePool...

Resource getResource() { Resource result;

try {

result = (Resource) _available.pop();

_allocated.push(result);

return result;

} catch (EmptyStackException e) { result = new Resource();

_allocated.push(result);

return result;

} }

Stack _available;

Stack _allocated;

In diesem Fall ist es nicht auòergewửhnlich, wenn die Ressourcen nicht ausrei- chen, also sollte ich keine Ausnahmen verwenden.

Um die Ausnahme zu entfernen, stelle ich als Erstes einen if-then-else-Block voran und verschiebe das leere Verhalten dort hin:

Resource getResource() { Resource result;

if (_available.isEmpty()) { result = new Resource();

_allocated.push(result);

return result;

} else { try {

result = (Resource) _available.pop();

_allocated.push(result);

return result;

} catch (EmptyStackException e) { result = new Resource();

_allocated.push(result);

return result;

} } }

So sollte diese Ausnahme nie auftreten. Ich kann eine Zusicherung einfügen, um das zu prüfen:

Resource getResource() { Resource result;

if (_available.isEmpty()) { result = new Resource();

_allocated.push(result);

return result;

} else { try {

result = (Resource) _available.pop();

_allocated.push(result);

return result;

} catch (EmptyStackException e) {

Assert.shouldNeverReachHere("available was empty on pop");

result = new Resource();

_allocated.push(result);

return result;

} } }

class Assert...

static void shouldNeverReachHere(String message) { throw new RuntimeException (message);

}

Nun kann ich umwandeln und testen. Wenn alles gut geht, kann ich den try- Block vollstọndig entfernen.

Resource getResource() { Resource result;

if (_available.isEmpty()) { result = new Resource();

_allocated.push(result);

return result;

} else {

result = (Resource) _available.pop();

_allocated.push(result);

return result;

} }

Anschlieòend stelle ich meistens fest, dass ich die Bedingungen vereinfachen kann. Hier kann ich Redundante Bedingungsteile konsolidieren (247) anwenden:

Resource getResource() { Resource result;

if (_available.isEmpty()) result = new Resource();

else

result = (Resource) _available.pop();

_allocated.push(result);

return result;

}

11 Der Umgang mit der Generalisierung

Die Generalisierung produziert ihren eigenen Satz von Refaktorisierungen, von denen die meisten mit dem Verschieben von Methoden in der Vererbungshierar- chie zu tun haben. Feld nach oben verschieben (330) und Methode nach oben verschie- ben (331) befửrdern Funktionen die Hierarchie hinauf; Feld nach unten verschieben (339) und Methode nach unten verschieben (337) befửrdern sie nach unten. Kon- struktoren sind etwas schwieriger die Hierarchie hinaufzuschieben, deshalb be- schọftigt sich Konstruktorrumpf nach oben verschieben (334) mit diesem Thema. An- statt einen Konstruktor nach oben zu verschieben, ist es oft sinnvoll, Konstruktor durch Fabrikmethode ersetzen (313) zu verwenden.

Wenn Sie verschiedene Methoden mit ọhnlicher Struktur, aber variierenden De- tails haben, kửnnen Sie Template-Methode bilden (355), um die Unterschiede von den Gemeinsamkeiten zu trennen.

Sie kửnnen nicht nur Methoden in der Hierarchie verschieben, sondern auch die Hierarchie durch die Bildung neuer Klassen verọndern. Unterklasse extrahieren (340), Oberklasse extrahieren (346) und Schnittstelle extrahieren (351) machen all dies, indem sie an verschiedenen Punkten ansetzen, um neue Elemente zu bilden.

Schnittstelle extrahieren (351) ist besonders dann wichtig, wenn Sie einen kleinen Teil der Funktionalitọt fỹr das Typsystem herausgreifen wollen. Stellen Sie fest, dass Sie unnửtige Klassen in Ihrer Hierarchie haben, so kửnnen Sie Hierarchie ab- flachen (354) einsetzen, um sie zu entfernen.

Manchmal stellen Sie fest, dass Vererbung nicht der beste Weg ist, eine Situation zu behandeln, und müssen statt dessen die Delegation verwenden. Vererbung durch Delegation ersetzen (363) hilft Ihnen bei dieser Änderung. Manchmal ist es im Leben aber anders und Sie müssen Delegation durch Vererbung ersetzen (363).

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

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

(468 trang)