Gehen Sie wie folgt vor, um Operationen zu implementieren:
-
Wählen Sie einen Algorithmus aus.
-
Wählen Sie passende Datenstrukturen für den Algorithmus aus.
-
Definieren Sie neue Klassen und Operationen gemäß Ihren Anforderungen.
-
Codieren Sie die Operation.
Algorithmus auswählen
Viele Operationen sind so einfach, dass Sie aus der Operation und ihrer Spezifikation implementiert werden können.
Nicht triviale Algorithmen werden hauptsächlich aus zwei Gründen benötigt: zum Implementieren komplexer Operationen,
für die eine Spezifikation existiert, und zum Optimieren von Operationen, denen ein einfacher aber ineffizienter
Algorithmus als Definition dient.
Passende Datenstrukturen für den Algorithmus auswählen
Die Auswahl von Algorithmen beinhaltet auch die Auswahl der zugehörigen Datenstruktur. Viele
Implementierungsdatenstrukturen sind Containerklassen, z. B. Arrays, Listen, Warteschlangen, Gruppen, Bags und
Variationen dieser Komponenten. Die meisten objektorientierten Sprachen und Programmierumgebungen stellen
Klassenbibliotheken mit dieser Art von wieder verwendbaren Komponenten zur Verfügung.
Neue Klassen und Operationen gemäß den Anforderungen definieren
Neue Klassen können beispielsweise definiert werden, um Zwischenergebnisse zu speichern. Einer Klasse können neue
Operationen niedriger Ebenen hinzugefügt werden, um eine komplexe Operationen aufzugliedern. Diese Operationen sind
häufig private Operationen der Klasse, d. h. sie sind außerhalb der Klasse nicht sichtbar.
Operation codieren
Schreiben Sie den Code für die Operation beginnend mit der Schnittstellenanweisung. Befolgen Sie die geltenden
Richtlinien für die Programmierung.
Der Zustand eines Objekts kann durch Referenz auf die Werte seines Attributs implementiert werden, ohne spezielle
Darstellung. Die Zustandsübergänge für ein solches Objekt sind in den sich ändernden Werten des Attributs impliziert
und das wechselnden Verhalten wird über bedingte Anweisungen programmiert. Diese Lösung ist für ein komplexes
Verhalten nicht geeignet, weil sie normalerweise zu komplexen Strukturen führt, die nur schwer geändert werden können,
wenn weitere Zustände hinzugefügt werden oder sich das Verhalten ändert.
Wenn das Verhalten des Designelements (oder seiner Bestandteile) vom Zustand abhängig ist, sind normalerweise ein oder
mehrere Zustandsdiagramme vorhanden, die das Verhalten der Modellelemente im Designelement beschreiben. Diese
Zustandsdiagramme dienen während der Implementierung als wichtige Eingabe.
Die in den Zustandsdiagrammen gezeigten Zustandsmaschinen machen den Zustand eines Objekts explizit, und die Übergänge
und das erforderliche Verhalten sind klar erkennbar. Eine Zustandsmaschine kann auf folgende Arten implementiert
werden:
-
Einfache Zustandsmaschinen können implementiert werden, indem ein Attribut definiert wird, das die möglichen
Zustände auflistet, und indem das Attribut das Verhalten für die ankommende Nachricht auswählt, beispielsweise in
einer Switch-Anweisung in Java oder C++. Diese Lösung lässt sich nicht gut auf komplexe Zustandsmaschinen
übertragen und und kann eine niedrige Laufzeitleistung verursachen. Ein Beispiel für diese Methode finden Sie in
[DOUG98], Kapitel 4, 4.4.3.
-
Komplexere Zustandsmaschinen können das Zustandsmuster verwenden. Eine Beschreibung des Zustandsmusters finden Sie
in [GAM94]. Diese Lösung wird auch in [DOUG98], Kapitel
6, 6.2.3, State Pattern, erläutert.
-
Eine Lösung auf der Basis von Tabellen ist gut geeignet für sehr komplexe Zustandsmaschinen, bei denen eine
Änderung auf einfache Weise durchführbar sein muss. Bei dieser Lösung sind für jeden Zustand Einträge in einer
Tabelle vorhanden, wobei jeder Eintrag die Eingaben den aufeinanderfolgenden Zuständen und zugehörigen
Übergangsaktionen zuordnet. Ein Beispiel für diese Methoden finden Sie unter [DOUG98], Kapitel
6, 6.2.3, State Table Pattern,.
Zustandsmaschinen mit mehreren gleichzeitig vorhandenen Unterzuständen können implementiert werden, indem die
Zustandsverwaltung an aktive Objekte delegiert wird (jeweils ein aktives Objekt für jeden Unterzustand), weil
gleichzeitig vorhandene Unterzustände unabhängige Verarbeitungen repräsentieren (die trotzdem interagieren können).
Jeder Unterzustand kann mittels einer der oben erläuterten Vorgehensweisen verwaltet werden.
Wenn eine Klasse oder Teile einer Klasse durch Wiederverwendung einer vorhandenen Klasse implementiert werden können,
verwenden Sie die Delegierung anstelle der Vererbung.
Delegierung bedeutet, dass die Klasse mit Hilfe anderer Klassen implementiert wird. Die Klasse referenziert mittels
einer Variablen ein Objekt einer anderen Klasse. Wenn eine Operation aufgerufen wird, ruft sie für die tatsächliche
Ausführung eine Operation im referenzierten Objekt (der wieder verwendeten Klasse) auf. Auf diese Weise wird die
Zuständigkeit an die andere Klasse übertragen.
Eine einseitige Assoziation wird als Zeiger implementiert, d. h. als Attribut, das eine Objektreferenz enthält. Ist die
Multiplizität gleich one, dann wird sie als einfacher Zeiger implementiert. Ist die Multiplizität gleich
many, dann ist sie eine Menge von Zählern. Ist die many-Seite geordnet, dann kann anstelle einer Menge
eine Liste verwendet werden.
Eine zweiseitige Assoziation wird als Attribute in beide Richtungen implementiert, wobei Techniken der einseitigen
Assoziation verwendet werden.
Ein qualifizierte Assoziation wird als Referenztabelle im Qualifikationsobjekt implementiert (z. B. eine
Smalltalk-Dictionary-Klasse). Die Selektionswerte in der Referenztabelle sind die Qualifikationsmerkmale und die
Zielwerte sind die Objekte der anderen Klasse.
Falls ein geordneter Zugriff auf die Qualifikationsmerkmale erforderlich ist, können diese in einem sortierten Array
oder einer sortierten Baumstruktur angeordnet werden. In diesem Fall ist die Zugriffszeit proportional zum
Protokollieren von N, wobei N die Zahl der Qualifikationsmerkmale ist.
Werden die Qualifikationsmerkmale aus einer kompakten finiten Menge abgerufen, dann können die Qualifikationsmerkmale
einem Integer-Bereich zugeordnet werden, und die Assoziation kann effizient als Array implementiert werden. Diese
Lösung ist besser geeignet, wenn die Assoziation meistens voll anstatt wenig gefüllt ist, und ist ideal für vollständig
gefüllte finite Mengen.
Die meisten objektorientierten Sprachen und Programmierumgebungen stellen Klassenbibliotheken mit wieder verwendbaren
Komponenten zur Verfügung, um unterschiedliche Arten von Assoziationen zu implementieren.
Attribute werden auf eine von drei Arten implementiert: durch Verwendung von integrierten primitiven Typen, durch
Verwendung einer vorhandenen Klasse oder durch Definition einer neuen Klasse. Die Definition einer neuen Klasse stellt
eine flexiblere Lösung dar, bewirkt aber eine unnötige Dereferenzierung. Beispielsweise kann die
Sozialversicherungsnummer eines Mitarbeiters entweder als Attribut des Typs "String" (Zeichenfolge) oder als neue
Klasse implementiert werden.
Alternative Implementierungen eines Attributs.
Es kann auch vorkommen, dass Gruppen von Attributen wie im folgenden Beispiel zu neuen Klassen kombiniert werden. Beide
Implementierungen sind korrekt.
Die Line-Attribute werden als Assoziationen zu einer Point-Klasse implementiert.
|