Zur Webseite der Informatik

Heuristiken für den objektorientierten Entwurf

Eine Heuristik ist eine Daumenregel, die in vielen Fällen funktioniert, in manchen aber auch nicht. Daher ist eine Heuristik im Gegensatz zum Prinzip nicht allgemeingültig. In der Praxis ist die Unterscheidung von Prinzip (allgemeingültige Entwurfsregel) und Heuristik (Daumenregel, die nicht immer gelten muss) ausgesprochen unscharf: Lakos (1996) zum Beispiel verwendet statt Prinzip den Begriff ,design rule"; dafür bedeutet für ihn ,principle" das, was hier als Heuristik bezeichnet wird. Für Prinzipien und Heuristiken gleichermaßen gilt aber, dass sie nicht blind anzuwenden sind, sondern letztlich anhand des Kontextes entschieden werden muss, ob ihr Einsatz an einer bestimmten Stelle im Entwurf sinnvoll ist.

Eine der ersten Sammlungen von Heuristiken stammt von Korson und McGregor (1990, S. 54). Riel (1996) veröffentlichte die bisher umfangreichste Sammlung, aber auch Booch et al. (1998) führen in ihrem UML-Handbuch viele Heuristiken auf. Firesmith (1995) enthält eine umfangreiche Liste von Heuristiken zur Vererbung. Weitere Heuristiken finden sich z. B. bei Lakos (1996) sowie Johnson und Foote (1988).

Die Heuristiken lassen sich in fünf Bereiche einteilen: Heuristiken für Klassen, Interfaces, Pakete, Vererbungsbeziehungen und sonstige Beziehungen.

Heuristiken für Klassen

Für eine Klasse sollte gelten:

  • Sie ist eine abgegrenzte Abstraktion eines Begriffs aus dem Problem- oder Lösungsbereich (Booch et al., 1998).
  • Sie enthält eine kleine, wohldefinierte Menge von Verantwortlichkeiten und führt diese alle gut aus (Booch et al., 1998).
  • Ihre Attribute und Methoden sind (wirklich) notwendig, um die Verantwortlichkeiten der Klasse zu realisieren (Booch et al., 1998).
  • Sie enthält eine Klasse zu viele Verantwortlichkeiten, sollten diese auf neue Klassen aufgeteilt werden (Booch et al., 1998).
  • Sie enthält eine Klasse zu wenig Verantwortlichkeiten, sollte sie mit anderen Klassen zusammengefaßt werden (Booch et al., 1998).
  • Sie trennt eindeutig zwischen der Schnittstelle und der Implementierung der Abstraktion (Booch et al., 1998).
  • Sie ist verständlich und einfach, aber trotzdem erweiterbar und anpassbar (Booch et al., 1998).
  • Ihre öffentliche Schnittstelle sollte nur aus Operationen bestehen (Korson, McGregor, 1990).
  • Operationen sollten möglichst wenig Parameter haben. (Johnson, Foote, 1998)
  • Attribute sollten verborgen sein. (Johnson, Foote, 1998)
  • Jede Methode sollte Attribute der Klasse verwenden (lesend oder schreibend) (Korson, McGregor, 1990)

Heuristiken für Interfaces

Für ein Interface sollte gelten:

  • Es ist einfach, aber trotzdem vollständig (Booch et al., 1998).
  • Es stellt alle nötigen (aber nicht mehr) Operationen für einen einzigen Dienst zur Verfügung (Booch et al., 1998).
  • Es ist verständlich, d. h. stellt genügend Information zur Verwendung und zur Implementierung zur Verfügung (Booch et al., 1998).
  • Es ist zugänglich, d. h. der Verwender kann die Haupteigenschaften verstehen, ohne durch eine Vielzahl von Operationen überwältigt zu werden (Booch et al., 1998).

Heuristiken für Pakete

Für ein Paket sollte gelten:

  • Es hat einen hohen Zusammenhalt und ist eine klare Abgrenzung einer Menge zusammengehöriger Elemente (Booch et al., 1998).
  • Es ist mit anderen Paketen lose gekoppelt, exportiert nur die Elemente, die andere Pakete wirklich sehen müssen und importiert nur wirklich benötigte Elemente aus anderen Paketen (Booch et al., 1998).
  • Es ist nicht zu tief verschachtelt - wegen der menschlichen Beschränkung bei der Erfassung von Verschachtelungen (Booch et al., 1998).
  • Die Anzahl der enthaltenen Elemente ist weder zu groß noch zu klein (Booch et al., 1998).
  • Zwischen Paketen soll es keine zyklischen Abhängigkeiten geben. Treten diese auf, sollte eines der Pakete zerschlagen werden, um den Zyklus aufzulösen. (Acyclic Dependencies Principle, ADP; Martin, 1996)
  • Die Gruppierung von Klassen in Pakete soll die Propagierung von Änderungen innerhalb eines Pakets in andere Pakete minimieren. Falls eine Änderung nötig sein sollte, sollten alle Klassen innerhalb des Pakets gemeinsam betroffen sein (Common Closure Principle, CCP; Martin, 1996).
  • Ein Paket sollte nur von Paketen abhängen, die stabiler sind als es selbst (Stable Dependency Principle, SDP; Martin, 1997). Dann wird es wenig Änderungen geben, die von Änderungen des verwendeten Paketes verursacht werden. Pakete, die sich häufig ändern, verursachen so keine Folgeänderungen in stabileren Paketen.
  • Je stabiler ein Paket ist, desto abstrakter sollte es sein. (Stable Abstractions Principle, SAP; Martin, 1997). Ein Paket ist abstrakt, wenn es nur abstrakte Klassen enthält. Abstrakte Klassen lassen sich leichter erweitern als konkrete Klassen, da es keine Einschränkungen durch eine vorhandene Implementierung gibt.
  • Ein Paket hat einen hohen Zusammenhalt, wenn die Klassen 
    (1) zusammen geschlossen sind, 
    (2) zusammen wiederverwendet werden (Common Reuse Principle, Martin 1996) und 
    (3) eine Funktion oder Verfahrensweise (policy) gemeinsam haben. 
    Die Punkte sind dabei von abnehmender Relevanz. (Martin, 1995)

Heuristiken für Vererbungsbeziehungen

Für Vererbungsbeziehungen sollte gelten:

  • Vererbung kann häufig durch Aggregation ersetzt werden (Booch et al., 1998).
  • Vererbungshierachien sollten balanciert sein: nicht tiefer als (etwa) fünf Stufen und nicht zu breit. Um die Breite zu reduzieren, können zur Gruppierung abstrakte Zwischenklassen in die Vererbungshierarchie einfügt werden (Booch et al., 1998).
  • Vererbung sollte nur verwendet werden, um eine Spezialisierungshierarchie zu modellieren (Riel, 1996).
  • Basisklassen sollten abstrakt sein (Riel, 1996)
  • Mehrfachvererbung sollte zunächst als Entwurfsfehler angesehen werden, bis das Gegenteil bewiesen ist (Riel, 1996).
  • Die Architektur soll die Details bestimmen, nicht umgekehrt. Das bedeutet, dass die allgemeinen Konzepte (z.B. in Form von Interfaces) "oben" in der Vererbungshierarchie, spezielle Konzepte (Implementierungen) "unten" angeordnet werden. (Booch et al., 1998).
  • Vererbungshierarchien sollten auf ein Paket beschränkt sein, d. h. sie sollten nicht paketübergreifend sein.
  • Unterklassen sollen Eigenschaften hinzufügen, aber nicht löschen. (Firesmith, 1995)
  • Oberklassen sollten nicht ihre Unterklassen verwenden. (Firesmith, 1995)

Heuristiken für sonstige Beziehungen

Für Beziehungen sollte gelten:

  • Benutzt-Beziehungen sollen nur dann verwendet werden, wenn es sich nicht um eine strukturelle Beziehung handelt. Letztere sollten durch Assoziationen modelliert werden (Booch et al., 1998).
  • Bei Aggregation sollte eine Klasse wissen, was sie enthält, aber sie sollte nie wissen, wer sie enthält (Riel, 1996)
  • Eine Klasse sollte von so wenig wie möglich anderen Klassen abhängen (Korson, McGregor, 1990)

Literatur

Booch et al. (1998): Booch, G.; Rumbaugh, J.; Jacobson, I.: The Unified Modeling Language User Guide. Addison-Wesley, Reading, MA, 1998.

Firesmith (1995): Firesmith, D.: Inheritance Guidelines. Journal of Object-Oriented Programming, 8(2), 1995, 67-72.

Johnson, Foote (1988): Johnson, R.; Foote, B.: Designing Reusable Classes. Journal of Object-Oriented Programming, 1(2), 1988, 22-35.

Korson, McGregor (1990): Korson, T.; McGregor, J.: Understanding Object-Oriented: A Unifying Paradigm. Communications of the ACM, 33(9), 1990, 40-60.

Lakos (1996): Lakos, J.: Large-Scale C++ Software Design. Addison-Wesley, Reading, MA, 1996.

Martin (1995): Martin, R.: Designing Object-Oriented C++ Applications Using the Booch Method. Prentice Hall, Englewood Cliffs, NJ, 1995.

Martin (1996): Martin, R.: Granularity. C++ Report, 8(11), 1996.http://www.objectmentor.com/publications/granularity.pdf

Martin (1997): Martin, R.: Stability. C++ Report, 9(2), 1997. http://www.objectmentor.com/publications/stability.pdf

Riel (1996): Riel, A.: Object-Oriented Design Heuristics. Addison-Wesley, Reading, MA, 1996.