Richtlinie: Generalisierung
Generalisierung ist eine Klassenbeziehung, die Eigenschaften erfasst, die mehrere Klassen gemein haben. Diese Richtlinie veranschaulicht, wie Sie diese Beziehung verwenden.
Beziehungen
Zugehörige Elemente
Hauptbeschreibung

Generalisierung

Viele Dinge im echten Leben haben gewisse Eigenschaften gemein. Hunde und Katzen sind beispielsweise beide Tiere. Auch Objekte können gemeinsame Eigenschaften haben, die Sie mit Hilfe einer Generalisierung zwischen ihren Klassen verdeutlichen können. Wenn Sie gemeinsame Merkmale in eigene Klassen extrahieren, lässt sich das System künftig einfacher verwalten.

Eine Generalisierung zeigt, dass eine Klasse von einer anderen erbt. Die erbende Klasse wird Nachfahre (oder untergeordnete Klasse) genannt. Die vererbende Klasse ist der Vorfahre (oder übergeordnete Klasse). Vererbung bedeutet, dass die Definition des Vorfahren, einschließlich aller Eigenschaften wie Attribute, Beziehungen und Operationen für die zugehörigen Objekte, auch für die Objekte des Nachfahren gilt. Die Generalisierung wird von der untergeordneten Klasse (Nachfahre) hin zur übergeordneten Klasse (Vorfahre) gezeichnet.

Generalisierung kann in mehreren Stufen stattfinden, was Ihnen ermöglicht, komplexe, mehrschichtige Vererbungshierarchien zu modellieren. Allgemeine Eigenschaften können in den oberen Teil der Vererbungshierarchie und besondere Eigenschaften in den unteren Teil gestellt werden. Anders ausgedrückt, Sie können mit Generalisierungen spezialisierte Versionen eines allgemeineren Konzepts modellieren.

Beispiel

In dem System "Recycling-Maschine" beschreiben alle Klassen - Dose, Flasche und Fass - unterschiedliche Typen von Pfandartikeln. Abgesehen von der Tatsache, dass alle Pfandartikel sind, haben sie noch zwei gemeinsame Eigenschaften: Höhe und Gewicht. Sie können diese Eigenschaften mit Attributen und Operationen in einer separaten Klasse, z. B. Pfandartikel, modellieren. Dose, Flasche und Fass erben die Eigenschaften dieser Klasse.

Im Begleittext beschriebene Abbildung

Die Klassen Dose, Flasche und Fass haben die gemeinsamen Eigenschaften Höhe und Gewicht. Jede dieser Klassen ist eine spezialisierte Version des allgemeinen Konzepts "Pfandartikel".

Mehrfachvererbung

Mit Mehrfachvererbung kann eine Klasse Eigenschaften von mehreren anderen Klassen erben, obwohl dies im Allgemeinen eher die Ausnahme ist.

Bei der Verwendung von Mehrfachvererbung müssen diverse potenzielle Probleme berücksichtigt werden:

  • Wenn die Klasse von mehreren Klassen erbt, müssen Sie prüfen, wie die Beziehungen, Operationen und Attribute in den Vorfahren benannt sind. Sollte derselbe Name in mehreren Vorfahren vorkommen, müssen Sie beschreiben, was dieser Name für die jeweilige erbende Klasse bedeutet, z. B. indem Sie den Namen mit der Deklarationsquelle qualifizieren.
  • Wenn Sie mit wiederholter Vererbung arbeiten, erbt ein Nachfahre in denselben Vorfahren mehrfach. In einem solchen Fall ist die Vererbungshierarchie, wie im Folgenden gezeigt, mit einem Symbol in Form einer Raute gekennzeichnet.

Im Begleittext beschriebene Abbildung

Wiederholte Vererbung und Mehrfachvererbung. Die Klasse "Fenster mit Schiebeleiste und Dialogfenster" erbt die Klasse "Fenster" mehrfach.

Eine Frage, die in diesem Kontext aufkommen kann, ist "Wie viele Kopien der Attribute von Fenster sind in Instanzen von Fenster mit Schiebeleiste und Dialogfenster enthalten?". Wenn Sie mit wiederholter Vererbung arbeiten, müssen Sie eine klare Definition der Semantik haben. In den meisten Fällen wird dies von der Programmiersprache definiert, die Mehrfachvererbung unterstützt.

Im Allgemeinen sind die Regeln der Programmiersprache, die die Mehrfachvererbung steuern, sehr komplex und häufig schwierig anzuwenden. Deshalb wird empfohlene, Mehrfachvererbung nur bei Bedarf und immer mit Vorsicht anzuwenden.

Abstrakte und konkrete Klassen

Eine Klasse, die nicht instanziert ist und nur als Vererbungsklasse für andere Klassen existiert, ist eine abstrakte Klasse. Klassen, die instanziert werden, sind konkrete Klassen. Eine abstrakte Klasse muss mindestens einen Nachfahren haben, um von Nutzen zu sein.

Beispiel

Ein Palettenbereich im Lagerverwaltungssystem ist eine abstrakte Entitätsklasse, die Eigenschaften darstellt, die verschiedenen Typen Palettenbereichen gemein sind. Die Klasse wird von den konkreten Klassen Station, Transporter und Lagereinheit geerbt, die alle Palettenbereiche in dem Lager sein können. Alle diese Objekte haben eine gemeinsame Eigenschaft: Sie können eine oder mehrere Paletten aufnehmen.

Im Begleittext beschriebene Abbildung

Die geerbte Klasse, hier Palettenbereich, ist abstrakt und wird selbst nicht instanziert.

Verwendung

Da Klassenstereotypen unterschiedliche Zwecke haben, macht die Vererbung zwischen einem Klassenstereotyp und einem keinen Sinn. Wenn Sie beispielsweise eine Grenzklasse eine Entitätsklasse erben lassen, würde aus der Grenzklasse eine Art Hybridklasse. Deshalb sollten Generalisierungen nur zwischen Klassen desselben Stereotyps verwendet werden.

Sie können Generalisierungen verwenden, um zwei Beziehungen zwischen Klassen auszudrücken:

  • Subtyping: Diese Art von Beziehung gibt an, dass der Nachfahre ein Subtyp des Vorfahren ist. Die Erstellung von Subtypen bedeutet, dass der Nachfahre die Struktur und das Verhalten des Vorfahren erbt und dass der Nachfahre ein Typ des Nachfahren ist (d. h. der Nachfahre ist ein Subtyp, der in jeder Situation für alle seine Vorfahren einspringen kann).
  • Subclassing: Diese Art von Beziehung gibt an, dass der Nachfahre eine Unterklasse (kein Subtyp) des Vorfahren ist. Subclassing bedeutet, dass der Nachfahre die Struktur und das Verhalten des Vorgänger erbt und dass der Nachfahre kein Typ des Vorfahren ist.

Sie können solche Beziehungen erstellen, indem Sie gemeinsame Merkmale untergliedern und in separate Klassen stellen, die die anderen erben, oder indem Sie neue Klassen erstellen, die eine spezialisierte Version allgemeinerer Klassen sind, und diese von den allgemeinen Klassen erben lassen.

Wenn die zwei Varianten gleich sind, es ist nicht schwierig, die richtige Vererbung zwischen Klassen einzurichten. Manchmal sind die Varianten jedoch nicht gleich, und Sie müssen darauf achten, dass die Vererbung verständlich bleibt. Zumindest müssen Sie den Zweck jeder Vererbungsbeziehung im Modell kennen.

Vererbung für die Unterstützung von Polymorphie

Subtyping bedeutet, dass der Nachfahre ein Subtyp ist, der jederzeit für alle seine Vorfahren einspringen kann. Subtyping ist eine spezielle Form der Polymorphie und eine wichtige Eigenschaft, da Sie mit ihr alle Clients (Objekte, die den Vorfahren verwenden) entwerfen können, ohne die potenziellen Nachfahren berücksichtigen zu müssen. Dies macht die Clientobjekte allgemeiner und wiederverwendbar. Wenn der Client das eigentliche Objekt verwendet, geht er dabei auf eine spezielle Weise vor, aber das Objekt führt immer seine Aufgabe aus. Durch Subtyping kann sichergestellt werden, dass das System Änderungen in der Gruppe der Subtypen toleriert.

Beispiel

In einem Lagerverwaltungssystem definiert die Klasse Transporterschnittstelle die grundlegende Funktionalität für die Kommunikation mit allen Typen von Transporteinrichtungen, z. B. Kränen und LKW. Die Klasse definiert unter anderem die Operation executeTransport.

Im Begleittext beschriebene Abbildung

Die Klassen LKW-Schnittstelle und Kranschnittstelle erben von der Transporterschnittstelle, d. h. Objekte beider Klassen antworten auf die Nachricht executeTransport. Die Objekte können jederzeit für die Transporterschnittstelle einspringen und dasselbe Verhalten bieten. Somit können andere Objekte (Clientobjekte) eine Nachricht an ein Objekt von Transporterschnittstelle senden, ohne zu wissen, ob ein Objekt von LKW-Schnittstelle oder Kranschnittstelle auf die Nachricht antwortet.

Die Klasse Transporterschnittstelle kann auch eine abstrakte Schnittstelle sein, die selbst nicht instanziert wird. In diesem Fall kann die Transportschnittstelle nur die Deklaration der Operation executeTransport definieren, während die untergeordneten Klassen (Nachfahren) die Operation implementieren.

Einige objektorientierte Sprachen wie C++ verwenden die Klassenhierarchie als eine Typhierarchie und zwingen den Designer damit, Vererbung für Subtyping im Designmodell einzusetzen. Andere Sprachen wie Smalltalk-80 verwenden zur Kompilierzeit keine Typprüfung. Wenn die Objekte auf eine empfangene Nachricht nicht antworten können, generieren Sie eine Fehlernachricht.

Es empfiehlt sich unter Umständen, auch in Sprachen ohne Typprüfung mit Generalisierung zu arbeiten, um Subtypbeziehungen anzuzeigen. In einigen Fällen sollten Sie unabhängig davon, ob die Sprache es zulässt, Generalisierung einsetzen, damit das Objektmodell und der Quellcode verständlicher werden und einfacher zu verwalten sind. Ob diese Verwendung von Vererbung dem guten Stil entspricht, richtet sich in erster Linie nach den Konventionen der Programmiersprache.

Vererbung zur Unterstützung der Wiederverwendung von Implementierungen

Subclassing macht den Wiederverwendungsaspekt von Generalisierungen aus. Wenn Sie mit Subclassing arbeiten, entscheiden Sie, welche Teile einer Implementierung Sie wiederverwenden können, indem Sie die Vererbung von Eigenschaften festlegen, die von anderen Klassen definiert werden. Mit Subclassing können Sie Aufwand einsparen und Code wiederverwenden, wenn Sie eine bestimmte Klasse implementieren.

Beispiel

In der Klassenbibliothek von Smalltalk-80 erbt die Klasse Dictionary Eigenschaften von Set.

Im Begleittext beschriebene Abbildung

Der Grund für diese Generalisierung ist der, dass Dictionary einige allgemeine Methoden und Speicherstrategien von der Implementierung von Set wiederverwenden kann. Selbst wenn ein Dictionary als ein Set (das Schlüssel/Wert-Paare enthält) gesehen werden könnte, ist Dictionary kein Subtyp von Set, da Sie nicht einfach einen beliebigen Typ von Objekt zu einem Dictionary (nur Schlüssel/Wert-Paare) hinzufügen können. Objekte, die Dictionary verwenden, wissen nicht, dass dass das Dictionary eigentlich ein Set ist.

Subclassing führt häufig zu unlogischen Vererbungshierarchien, die schwierig zu verstehen und zu verwalten sind. Deshalb wird davon abgeraten, Vererbung nur zum Zwecke der Wiederverwendung zu verwenden, sofern keine anderslautende Empfehlung für die Verwendung der Programmiersprache vorgegeben ist. Die Verwaltung dieser Art von Wiederverwendung ist in der Regel etwas knifflig. Jede Änderung in der Klasse Set kann umfangreiche Änderungen aller Klassen nach sich ziehen, die die Klasse Set erben. Bedenken Sie dies und vererben Sie nur stabile Klassen. Bei Vererbung wird die Implementierung der Klasse Set eingefroren, da Änderungen an dieser Klasse zu kostenintensiv sind.

Vererbung in Programmiersprachen

Die Verwendung von Generalisierungsbeziehungen im Design muss sich maßgeblich nach der Semantik und der empfohlenen Verwendung von Vererbung in der Programmiersprache richten. Objektorientierte Sprachen unterstützen Vererbung zwischen Klassen. Nicht objektorientierte Sprachen tun dies nicht. Sie müssen Sprachmerkmale im Designmodell berücksichtigen. Wenn Sie eine Sprache verwenden, die Vererbung oder Mehrfachvererbung nicht unterstützt, müssen Sie die Vererbung in der Implementierung simulieren. In diesem Fall ist es besser, die Simulation im Designmodell zu modellieren und auf Generalisierungen zu verzichten, um die Vererbungsstrukturen zu beschreiben. Die Modellierung von Vererbungsstrukturen mit Generalisierungen und anschließende Simulation der Vererbung in der Implementierung kann das Design ruinieren.

Wenn Sie eine Sprache verwenden, die Vererbung oder Mehrfachvererbung nicht unterstützt, müssen Sie die Vererbung in der Implementierung simulieren. In diesem Fall empfiehlt es sich, die Simulation im Designmodell zu modellieren und auf Generalisierungen zu verzichten, um die Vererbungsstrukturen zu beschreiben. Wenn Sie Vererbungsstrukturen mit Generalisierungen modellieren und anschließend nur die Vererbung in der Implementierung simulieren, kann dies das Design ruinieren.

Wahrscheinlich müssen Sie die Schnittstellen und weitere Objekteigenschaften während der Simulation ändern. Es wird empfohlen, Vererbung mit einer der folgenden Methoden zu simulieren:

  1. Lassen Sie den Nachfahren Nachrichten an den Vorfahren weiterleiten.
  2. Duplizieren Sie den Code des Vorfahren in jedem Nachfahren. In diesem Fall wird keine Vorfahrenklasse erstellt.

Beispiel

In diesem Beispiel leiten die Nachfahren Nachrichten über Links, die Instanzen von Assoziationen sind, an den Vorfahren weiter.

Im Begleittext beschriebene Abbildung

Verhalten, das Dose, Flasche und Fass gemein ist, wird einer speziellen Klasse zugeordnet. Objekte, die dieses Verhalten aufweisen, senden eine Nachricht an ein Objekt von Pfandartikel, um das Verhalten bei Bedarf zu erbringen.