Designmuster und -mechanismen verwenden
Verwenden Sie Designmuster und -mechanismen, die für die zu entwerfende Klasse bzw. Funktionalität geeignet sind und
den Designrichtlinien für das Projekt entsprechen.
Durch die Integration eines Musters und/oder Mechanismus werden viele der nachfolgend in dieser Aufgabe beschriebenen
Schritte (neue Klassen, Operationen, Attribute und Beziehungen hinzufügen) effizient und trotzdem gemäß den im Muster
bzw. Mechanismus definierten Regeln ausgeführt.
Beachten Sie, dass Muster und Mechanismen gewöhnlich während der Weiterentwicklung des Designs und nicht im ersten
Schritt dieser Aufgabe integriert werden. Sie werden außerdem häufig auf mehrere Klassen und nicht nur auf eine
einzelne Klasse angewendet.
|
Erste Designklassen erstellen
Erstellen Sie eine oder mehrere erste Designklassen für die in dieser Aufgabe zu erstellende Analyseklasse und ordnen
Sie Trace-Abhängigkeiten zu. Die in diesem Schritt erstellten Designklassen werden in den nachfolgenden Schritten
präzisiert, angepasst, aufgeteilt oder zusammengefasst, wenn ihnen verschiedene Designmerkmale wie Operationen,
Methoden und eine Zustandsmaschine zugeordnet wurden, die beschreiben, wie die Analyseklasse entworfen ist.
Je nach Typ der zu erstellenden Analyseklasse (Grenzklasse, Entitätsklasse oder Steuerungsklasse) stehen bestimmte
Strategien zur Verfügung, die Sie zum Erstellen erster Designklassen angewendet werden können.
Grenzklassen stellen Schnittstellen zu Benutzern oder anderen Systemen dar.
In der Regel werden Grenzklassen, die Schnittstellen zu anderen Systemen darstellen, als Subsysteme modelliert, weil
sie häufig ein komplexes internes Verhalten haben. Wenn das Schnittstellenverhalten einfach ist (z. B. nur als
Durchgang zu einer vorhandenen API zum externen System auftritt), können Sie die Schnittstelle mit einer oder mehreren
Designklassen darstellen. Wenn Sie diesen Weg wählen, verwenden Sie pro Protokoll, Schnittstelle oder API eine
Designklasse und dokumentieren Sie spezielle Anforderungen bezüglich Standards, die Sie in den Sonderanforderungen der
Klasse verwendet haben.
Für Grenzklassen, die Schnittstellen zu Benutzern darstellen, gilt im Allgemeinen die Regel, dass für jedes Fenster
bzw. jedes Formular in der Benutzerschnittstelle eine Grenzklasse verwendet wird. Deshalb sind die Zuständigkeiten der
Grenzklassen auf einer relativ hohen Ebene angesiedelt und müssen in diesem Schritt präzisiert und detailliert
beschrieben werden. Zusätzliche Modelle oder Prototypen der Benutzerschnittstelle können eine weitere Eingabequelle
sein, die in diesem Schritt berücksichtigt werden muss.
Das Design von Grenzklassen richtet sich nach den Entwicklungstools für Benutzerschnittstellen (UI), die im Projekt zur
Verfügung stehen. Bei der Verwendung aktueller Technologie wird die UI gewöhnlich direkt im Entwicklungstool visuell
erstellt. Dabei werden die UI-Klassen, die dem Design der Steuerungsklassen und Entitätsklassen zugeordnet werden
müssen, automatisch erstellt. Wenn die UI-Entwicklungsumgebung die unterstützenden Klassen automatisch erstellt, muss
es die UI implementieren, und daher besteht kein Bedarf, diese im Design zu berücksichtigen. Sie entwerfen nur das, was
die Entwicklungsumgebung nicht für Sie erstellt.
Während der Analyse stellen Entitätsklassen bearbeitete Informationseinheiten dar. Sie sind häufig passiv und
persistent und können mit den Analysemechanismen für Persistenz identifiziert und zugeordnet werden. Die Details des
Designs eines datenbankbasierten Persistenzmechanismus werden mit der Aufgabe:
Datenbankdesign abgedeckt. Leistungsaspekte können eine Umstrukturierung persistenter Klassen erfordern, was zu
Änderungen am Designmodell führt. Diese werden gemeinsam von Datenbankdesigner und Designer
beschlossen.
Eine allgemeinere Erläuterung von Designaspekten für persistente Klassen finden Sie unter der Überschrift Persistente Klassen identifizieren.
Ein Steuerungsobjekt ist für die Verwaltung des Ablaufs eines Anwendungsfalls verantwortlich und koordiniert somit die
meisten Aktionen des Anwendungsfalls. Steuerungsobjekte kapseln Logik, die in keinem besonderen Zusammenhang mit den
Benutzerschnittstellenaspekten (Grenzobjekte) oder Daten-Engineering-Aspekten (Entitätsobjekte) stehen. Diese Logik
wird auch Anwendungslogik oder Geschäftslogik genannt.
Berücksichtigen Sie beim Design von Steuerungsklassen folgende Punkte:
-
Komplexität - Unkompliziertes Steuerungs- oder Koordinationsverhalten kann mit Grenz- oder Entitätsklassen
bearbeitet werden. Mit steigender Komplexität der Anwendung zeichnen sich jedoch signifikante Nachteile dieses
Ansatzes auf, wie z. B.:
-
Das Koordinationsverhalten des Anwendungsfalls wird in die Benutzerschnittstelle eingebettet, wodurch das
System schwieriger zu ändern ist.
-
Eine Wiederverwendung derselben UI in verschiedenen Anwendungsfallrealisierungen ist nicht ohne Weiteres
möglich.
-
Die Benutzerschnittstelle wird mit zusätzlicher Funktionalität belastet, was sich auf die Leistung auswirkt.
-
Die Entitätsobjekte können mit anwendungsspezifischem Verhalten belastet werden, was ihre Allgemeingültigkeit
beeinträchtigt.
Zur Vermeidung solcher Probleme können Steuerungsklassen eingeführt werden, die Verhalten bereitstellen, das mit
der Koordination von Ereignisabläufen in Zusammenhang steht.
-
Änderungswahrscheinlichkeit - Wenn die Wahrscheinlichkeit, dass sich Ereignisabläufe ändern, gering ist oder
die Kosten zu vernachlässigen sind, können sich die Sonderausgaben und die Komplexität zusätzlicher
Steuerungsklassen nicht rechtfertigen.
-
Verteilung und Leistung - Der Bedarf, Teile der Anwendung auf unterschiedlichen Knoten oder in
unterschiedlichen Prozessbereichen auszuführen, bringt den Bedarf an speziellen Designmodellelementen mit sich.
Diese Spezialisierung wird häufig durch das Hinzufügen von Steuerungsobjekten und die Verteilung des Verhaltens von
den Grenz- und Entitätsklassen auf die Steuerungsklassen erreicht. Dadurch wird erreicht, dass die Grenzklassen nur
noch Benutzerschnittstellenservices, die Entitätsklassen nur noch Datenservices und die Steuerungsklassen die
restlichen Services bereitstellen.
-
Transaktionsmanagement - Die Verwaltung von Transaktionen ist eine klassische Koordinationsaktivitäten. Ohne
ein Framework, das das Transaktionsmanagement unterstützt, müssten eine oder mehrere
Transaktionsmanagerklassen miteinander interagieren, um sicherzustellen, dass die Integrität der Transaktion
gewahrt bleibt.
Wenn die Steuerungsklasse in den beiden letzten Fällen einen separaten Steuerungs-Thread darstellt, würde es sich
anbieten, eine aktive Klasse für die Modellierung des Steuerungs-Thread zu verwenden. In Echtzeitsystemen ist die
Verwendung von Kapseln der bevorzugte Modellierungsansatz.
|
Persistente Klassen identifizieren
Klassen, die ihren Zustand auf einem permanenten Medium speichern müssen, werden als persistente Klassen bezeichnet.
Der Zustand muss möglicherweise gespeichert werden, um Klasseninformationen permanent aufzuzeichnen, um eine Sicherung
im Fall eines Systemausfalls zu haben oder um Informationen auszutauschen. Eine persistente Klasse kann persistente und
transiente Instanzen haben. Eine Klasse als persistent zu bezeichnen, bedeutet lediglich, dass einige Instanzen der
Klasse persistent sein müssen.
Integrieren Sie Designmechanismen für die Persistenzmechanismen, die während der Analyse gefunden werden. Je nachdem,
was die Klasse erfordert, könnte der Analysemechanismus für Persistenz beispielsweise durch einen der folgenden
Designmechanismen realisiert werden:
-
Speicher im Hauptspeicher
-
Flash-Karte
-
Binärdatei
-
Datenbankmanagementsystem (DBMS)
Persistente Objekte können nicht nur von Entitätsklassen abgeleitet werden. Persistente Objekte können auch
erforderlich sein, um nicht funktionale Anforderungen im Allgemeinen zu bearbeiten. Beispiele hierfür sind persistente
Objekte, die erforderlich sind, um für die Prozesssteuerung relevante Informationen oder Zustandsinformationen zwischen
Transaktionen zu verwalten.
Die Identifizierung persistenter Klassen dient dazu, den Datenbankdesigner darüber zu informieren, dass die Klasse spezielle
Aufmerksamkeit in Bezug auf die physischen Speichermerkmale erfordert. Außerdem erhält der Softwarearchitekt eine Benachrichtigung, dass die Klasse persistent
sein muss, und der für den Persistenzmechanismus verantwortliche Designer eine
Benachrichtigung, dass die Instanzen der Klasse persistent sein müssen.
Da eine eine koordinierte Persistenzstrategie erforderlich ist, muss der Datenbankdesigner unter Verwendung eines Persistenz-Framework
persistente Klassen in der Datenbank abbilden. Wenn das Projekt ein Persistenz-Framework entwickelt, muss der
Framework-Entwickler sich außerdem mit den Persistenzanforderungen von Designklassen auseinandersetzen. Um diese
Personen mit den benötigten Informationen zu versorgen, reicht es an dieser Stelle aus, darauf hinzuweisen, dass die
Klasse persistent ist, oder genauer gesagt, dass die Instanzen der Klasse persistent sind.
|
Klassensichtbarkeit definieren
Bestimmen Sie für jede Klasse die Sichtbarkeit im übergeordneten Paket. Eine öffentliche Klasse kann außerhalb
des übergeordneten Pakets referenziert werden. Eine private Klasse (oder eine Klasse, deren Sichtbarkeit mit
Implementierung festgelegt ist) kann nur von Klassen in demselben Paket referenziert werden.
|
Operationen definieren
Gehen Sie wie folgt vor, um die Operationen in Designklassen zu identifizieren:
-
Studieren Sie die Zuständigkeiten jeder Analyseklasse und erstellen Sie für jede Zuständigkeit eine Operation.
Verwenden Sie die Beschreibung der Zuständigkeit als Ausgangsbeschreibung für die Operation.
-
Studieren Sie die Anwendungsfallrealisierungen in der Klasse partizipiert, um festzustellen, wie die
Operationen von den Anwendungsfallrealisierungen verwendet werden. Erweitern Sie die Operationen, eine
Anwendungsfallrealisierung nach der anderen, und präzisieren Sie die Operationen, ihre Beschreibungen, ihre
Rückgabetypen und ihre Parameter. Die Anforderungen jeder Anwendungsfallrealisierung in Bezug auf die Klassen
werden in Textform im Ereignisablauf der Anwendungsfallrealisierung beschrieben.
-
Studieren Sie die Sonderanforderungen, um sicherzustellen, dass keine impliziten Anforderungen an die Operation,
die dort beschrieben sind, vergessen wurden.
Operationen sind erforderlich, um die Nachrichten zu unterstützen, die in den Ablaufdiagrammen erscheinen, weil Scripts
(temporäre Nachrichtenspezifikationen, die noch keinen Operationen zugeordnet wurden) das Verhalten beschreiben, das
von der Klasse erwartet wird. Abbildung 1 zeigt ein Beispiel für ein Ablaufdiagramm.
Abbildung 1: Nachrichten bilden die Basis für die Identifizierung von Operationen
Anwendungsfallrealisierungen können nicht genügend Informationen enthalten, um alle Operationen zu identifizieren. Zum
Ermitteln der verbleibenden Operationen sollten Sie die folgenden Fragen verwenden:
-
Gibt es eine Möglichkeit, eine neue Instanz der Klasse zu initialisieren, z. B. auch durch Verbindung der Klasse
mit Instanzen anderer Klassen, denen sie zugeordnet ist?
-
Muss ein Test durchgeführt werden, um festzustellen, ob zwei Instanzen der Klasse identisch sind?
-
Muss eine Kopie einer Klasseninstanz erstellt werden?
-
Werden Operationen in der Klasse von Mechanismen benötigt? Ein Garbage-Collection-Mechanismus kann
beispielsweise voraussetzen, dass ein Objekt in der Lage ist, alle Referenzen auf alle anderen Objekten zu löschen,
damit nicht verwendete Ressourcen freigegeben werden können.
Definieren Sie keine Operationen, die lediglich Werte von öffentlichen Attributen abrufen (get) und setzen (set).
Weitere Informationen hierzu finden Sie in Attribute definieren und Assoziationen definieren. Gewöhnlich werden diese von Codegenerierungsfunktionen
generiert und müssen nicht explizit definiert werden.
Verwenden Sie für die Benennung von Operationen, Rückgabetypen und Parametern sowie ihren Typen die Namenskonventionen
der Implementierungssprache. Diese sind in den projektspezifischen Richtlinien beschrieben.
Sie müssen für jede Operation Folgendes definieren:
-
Operationsname - Verwenden Sie einen kurzen Namen, der das Ergebnis der Operation beschreibt.
-
Die Namen von Operationen müssen der Syntax der Implementierungssprache entsprechen. Beispiel:
position_suchen ist zwar für C++ und Visual Basic akzeptabel, aber nicht für Smalltalk (in dieser
Sprache werden keine Unterstreichungszeichen verwendet). Ein Name, der sich für alle Sprachen eignet, wäre
PositionSuchen.
-
Vermeiden Sie Namen, die implizieren, wie die Operation ausgeführt wird. Beispielsweise eignet sich
Mitarbeiter.loehne() besser als Mitarbeiter.LoehneBerechnen(), da letzterer darauf hinweise,
dass eine Berechnung durchgeführt wird. Die Operation könnte auch nur einfach einen Wert aus der Datenbank
zurückgeben.
-
Der Name einer Operation muss den Zweck der Operation deutlich machen. Vermeiden Sie unspezifische Namen
wie getDaten, die das Ergebnis der Operation nicht beschreiben. Verwenden Sie einen Namen, der exakt
zeigt, was erwartet wird, z. B. getAdresse. Noch besser ist, als Operationsnamen den Namen der
Eigenschaft zu verwenden, die zurückgegeben oder gesetzt wird. Wenn die Operation einen Parameter hat, wird
die Eigenschaft gesetzt. Wenn die Operation keinen Parameter hat, wird die Eigenschaft abgerufen. Beispiel:
Die Operation adresse gibt die Adresse eines Kunden zurück, wohingegen
adress(eineZeichenfolge) die Adresse des Kunden setzt oder ändert. Die get- und
set-Spezifik der Operation ergibt sich implizit aus der Deklaration der Operation.
-
Operationen, die konzeptionell identisch sind, sollten denselben Namen haben, selbst wenn sie von
unterschiedlichen Klassen definiert werden, auf unterschiedliche Arten implementiert werden oder eine
unterschiedliche Anzahl von Parametern haben. Eine Operation, die ein Objekt erstellt, sollte
beispielsweise in allen Klassen denselben Namen haben.
-
Wenn Operationen in mehreren Klassen dieselbe Deklaration haben, muss die Operation dieselbe Art von
Ergebnis für das jeweilige Empfängerobjekt zurückgeben. Dies ist ein Beispiel für das Konzept der
Polymorphie, das sagt, dass unterschiedliche Objekte in ähnlicher Form auf dieselbe Nachricht
antworten sollen. Beispiel: Die Operation name soll den Namen des Objekts zurückgeben, unabhängig
davon, wie der Name gespeichert ist oder abgeleitet wird. Die Einhaltung dieses Prinzips macht das Modell
verständlicher.
-
Rückgabetyp - Der Rückgabetyp muss die Klasse des Objekts sein, dass von der Operation zurückgegeben wird.
-
Kurzbeschreibung - So aussagefähig Sie den Namen einer Operation auch gestalten, ist er häufig nur wenig
hilfreich, um den Zweck einer Operation zu verstehen. Weisen Sie der Operation eine Kurzbeschreibung zu, die aus
einigen wenigen Sätzen besteht und aus der Sicht des Benutzers geschrieben ist.
-
Parameter - Erstellen Sie für jeden Parameter einen kurzen, beschreibenden Namen, legen Sie die zugehörige
Klasse fest und weisen Sie ihm eine Kurzbeschreibung zu. Denken Sie bei der Spezifikation von Parametern daran,
dass die Wiederverwendbarkeit umso höher ist, je weniger Parameter definiert sind. Eine kleine Anzahl von
Parametern macht die Operation verständlicher und erhöht die Wahrscheinlichkeit, ähnliche Operationen zu finden.
Möglicherweise müssen Sie eine Operation mit vielen Parametern in mehrere Operationen aufteilen. Die Operation muss
für diejenigen, von denen sie verwendet wird, verständlich sein. Die Kurzbeschreibung sollte Folgendes enthalten:
-
Die Bedeutung der Parameter, falls diese nicht aus den Namen hervorgeht.
-
Ob der Parameter nach Wert oder nach Referenz übergeben wird.
-
Parameter, für die Werte angegeben werden müssen.
-
Optionale Parameter und ihre Standardwerte, falls kein Wert angegeben wird.
-
Gültige Bereiche für Parameter, sofern anwendbar.
-
Was ist der Operation ausgeführt wird.
-
Welche Parameter, die nach Referenz übergeben werden, von der Operation geändert werden.
Nachdem Sie die Operationen definiert haben, vervollständigen Sie die Ablaufdiagramme mit den Informationen zu den
Operationen, die für jede Nachricht aufgerufen werden.
Weitere Informationen hierzu finden Sie im Abschnitt Richtlinie für Arbeitsergebnis: Designklasse.
Identifizieren Sie für jede Operation die Exportsichtbarkeit der Operation. Sie können zwischen den folgenden Optionen
wählen:
-
Public (Öffentlich) - Die Operation ist auch für andere Modellelemente abgesehen von der Klasse sichtbar.
-
Implementation (Implementierung) - Die Operation ist nur innerhalb der Klasse sichtbar.
-
Protected (Geschützt) - Die Operation ist nur für die Klasse selbst, ihre Unterklassen und für so genannte
Friends der Klasse (sprachenabhängig) sichtbar.
-
Private (Privat) - Die Operation ist nur für die Klasse selbst und für so genannte Friends der Klasse
sichtbar.
Wählen Sie die Sichtbarkeitsoption aus, die am restriktivsten ist und trotzdem die Zielsetzungen der Operationen
unterstützt. Schauen Sie sich dazu die Ablaufdiagramme an und bestimmen Sie für jede Nachricht, ob die Nachricht von
einer Klasse außerhalb des Empfängerpakets (erfordert die Sichtbarkeitsoption public), aus dem Paket selbst
(erfordert die Sichtbarkeitsoption implementation), von einer Unterklasse (erfordert die Sichtbarkeitsoption
protected) oder von der Klasse selbst oder einer Friend-Klasse stammt (erfordert die Sichtbarkeitsoption
private).
Meistens sind Operationen Instanzoperationen, d. h. sie werden in Instanzen der Klasse ausgeführt. Manchmal gilt eine
Operation jedoch für alle Instanzen der Klasse und ist damit eine klassenbezogene Operation. Der Empfänger der
Klassenoperation ist eine Instanz einer Metaklasse (Beschreibung der Klasse selbst) und keine spezifische
Instanz der Klasse. Beispiele für Klassenoperationen sind Nachrichten, die neue Instanzen erstellen (instanzieren), die
alle Instanzen einer Klasse zurückgeben.
Die Operationszeichenfolge ist unterstrichen, um sie als klassenbezogene Operation zu kennzeichnen.
|
Methoden definieren
Eine Methode spezifiziert die Implementierung einer Operation. In vielen Fällen, in denen das von der Operation
geforderte Verhalten ausreichend durch den Operationsnamen, die Beschreibung und die Parameter definiert wird, werden
Methoden direkt in der Programmiersprache implementiert. Wenn die Implementierung einer Operation die Verwendung eines
speziellen Algorithmus oder mehr Informationen erfordert, als die Beschreibung der Operation enthält, ist eine
gesonderte Methodenbeschreibung erforderlich. Die Methode beschreibt, wie die Operation funktioniert und
nicht nur, was sie tut.
Die Methode sollte Folgendes beschreiben:
-
Wie Operationen implementiert werden.
-
Wie Attribute implementiert und zur Implementierung von Operationen verwendet werden.
-
Wie Beziehungen implementiert und zur Implementierung von Operationen verwendet werden.
Die Anforderungen variieren von Fall zu Fall, aber die Methodenspezifikationen für eine Klasse müssen unter allen
Umständen Folgendes beschreiben:
-
Was in Bezug auf die Anforderungen unternommen wird.
-
Welche anderen Objekte und Operationen verwendet werden.
Spezifischere Anforderungen könnten sich auf Folgendes beziehen:
-
Wie Parameter implementiert werden.
-
Welche, sofern überhaupt, speziellen Algorithmen verwendet werden.
Ablaufdiagramme sind hierfür eine wichtige Quelle. Aus den Ablaufdiagrammen geht klar hervor, welche Operationen in
anderen Objekten verwendet werden, wenn eine Operation ausgeführt wird. Welche Operationen in anderen Objekten
verwendet werden, muss für eine vollständige Implementierung einer Operation spezifiziert werden. Die Erstellung einer
vollständigen Methodenspezifikation setzt demzufolge voraus, dass Sie die Operationen für die beteiligten Objekte
identifizieren und die entsprechenden Ablaufdiagramme untersuchen.
|
Zustände definieren
Bei einigen Operationen richtet sich ihr Verhalten nach dem jeweiligen Zustand des Empfängerobjekts. Eine
Zustandsmaschine ist ein Tool, das die Zustände beschreibt, die ein Objekt annehmen kann, und die Ereignisse, die
bewirken, dass das Objekt von einem Zustand in einen anderen wechselt. Informationen hierzu finden Sie im Abschnitt Technik: Zustandsdiagramm. Zustandsmaschinen sind hilfreich für die Beschreibung
aktiver Klassen. Die Verwendung von Zustandsmaschinen ist insbesondere wichtig, wenn das Verhalten von Kapseln definiert wird.
Ein Beispiel für eine einfache Zustandsmaschine wird in Abbildung 2 gezeigt.
Abbildung 2: Einfaches Zustandsdiagramm für eine Zapfsäule
Jedes Ereignis für einen Statusübergang kann einer Operation zugeordnet werden. Je nach Objektzustand kann die
Operation ein anderes Verhalten haben. Dies wird in den Übergangsereignissen beschrieben.
Die Methodenbeschreibung für die zugeordnete Operation muss mit den zustandsspezifischen Informationen
aktualisiert werden. Für jeden relevanten Zustand muss angegeben werden, was die Operation tun soll. Zustände werden
häufig mit Attributen dargestellt. Die Zustandsdiagramme dienen als Eingabe für die Attributidentifikation.
Weitere Informationen hierzu finden Sie im Abschnitt Technik:
Zustandsdiagramm.
|
Attribute definieren
Während der Definition von Methoden und der Identifizierung von Zuständen werden die Attribute ermittelt,
die die Klasse erfordert, um ihre Operationen auszuführen. Attribute sind ein Informationsspeicher für die
Klasseninstanz und werden häufig verwendet, um den Zustand der Klasseninstanz darzustellen. Für jede Information, die
die Klasse selbst verwaltet, wird ein Attribut verwendet. Definieren Sie für jedes Attribut Folgendes:
-
Den Namen, bei dem die Namenskonventionen der Implementierungssprache und des Projekts eingehalten werden
müssen.
-
Den Typ, bei dem es sich um einen Elementardatentyp handelt, der von der Implementierungssprache unterstützt
wird.
-
Den Standard- bzw. Anfangswert, mit dem das Attribut initialisiert wird, wenn neue Instanzen der Klasse
erstellt werden.
-
Die Sichtbarkeit, bei der Sie zwischen den folgenden Werten wählen können:
-
Public (Öffentlich): Das Attribut ist innerhalb und außerhalb des Pakets, das die Klasse enthält,
sichtbar.
-
Protected (Geschützt): Das Attribut ist nur für die Klasse selbst, ihre Unterklassen und für so
genannte Friends der Klasse (sprachenabhängig) sichtbar.
-
Private (Privat): Das Attribut ist nur für die Klasse selbst und für so genannte Friends der Klasse
sichtbar.
-
Implementation (Implementierung): Das Attribut ist nur für die Klasse selbst sichtbar.
-
Persistente Klassen, d. h. ob das Attribut persistent (Standardeinstellung) oder transient ist. Selbst wenn
die Klasse selbst persistent ist, müssen nicht alle Attribute der Klasse persistent sein.
Stellen Sie sicher, dass alle Attribute erforderlich sind. Attribute müssen gerechtfertigt sein. Attribute werden
häufig in einem frühen Stadium des Prozess hinzugefügt und bleiben aus Unachtsamkeit bestehen, obwohl sie nicht mehr
benötigt werden. Zusätzliche Attribute, multipliziert mit Tausenden oder Millionen von Instanzen, können nachteilige
Auswirkungen auf die Leistung und den Speicherbedarf eines Systems haben.
Weitere Informationen zu Attributen finden Sie im Abschnitt Attribute im Abschnitt Richtlinie für Arbeitsergebnis: Designklasse.
|
Abhängigkeiten definieren
Stellen Sie sich zu jedem Fall, in dem eine Kommunikation zwischen Objekten erforderlich ist, die folgenden Fragen:
-
Wird die Referenz auf den Empfänger als Parameter an die Operation übergeben? Wenn ja, richten Sie eine
Abhängigkeit zwischen den Sender- und Empfängerklassen in einem Klassendiagramm ein, das die beiden Klassen
enthält. Wenn ein Kommunikationsdiagramm für Interaktionen verwendet wird, müssen Sie außerdem die
Sichtbarkeit der Verbindung qualifizieren und auf parameter setzen.
-
Ist der Empfänger global? Wenn ja, richten Sie eine Abhängigkeit zwischen den Sender- und Empfängerklassen
in einem Klassendiagramm ein, das die beiden Klassen enthält. Wenn ein Kommunikationsdiagramm für
Interaktionen verwendet wird, müssen Sie außerdem die Sichtbarkeit der Verbindung qualifizieren und auf
global setzen.
-
Ist der Empfänger ein temporäres Objekt, das während der Operation selbst erstellt und wieder gelöscht wird? Wenn
ja, richten Sie eine Abhängigkeit zwischen den Sender- und Empfängerklassen in einem Klassendiagramm ein,
das die beiden Klassen enthält. Wenn ein Kommunikationsdiagramm für Interaktionen verwendet wird, müssen Sie
außerdem die Sichtbarkeit der Verbindung qualifizieren und auf local setzen.
Verbindungen, die auf diese Weise modelliert werden, sind transiente Verbindungen, die nur für eine begrenzte Dauer im
spezifischen Kontext der Kollaboration existieren. In diesem Sinne sind sie Instanzen der Assoziationsrolle in der
Kollaboration. Die Beziehung in einem Klassenmodell (d. h. kontextunabhängig) muss jedoch, wie bereits erwähnt, eine
Abhängigkeit sein. [RUM98] definiert
eine transiente Verbindung wie folgt: "Es ist möglich, solche Verbindungen generell als Assoziationen zu
modellieren, aber in diesem Fall müssen die Bedingungen in den Assoziationen sehr allgemein beschrieben werden und
verlieren damit einen Großteil ihrer Präzision bezüglich der Einschränkung von Objektkombinationen.". In dieser
Situation ist die Modellierung einer Abhängigkeit weniger wichtig als die Modellierung der Beziehung in der
Kollaboration, da die Abhängigkeit die Beziehung nicht vollständig beschreibt, sondern nur, dass sie existiert.
|
Assoziationen definieren
Assoziationen sind ein Mechanismus, der die Kommunikation zwischen Objekten ermöglicht. Sie stellen Objekten einen
Kanal zur Verfügung, über den Nachrichten gesendet werden können. Außerdem dokumentieren sie die Abhängigkeiten
zwischen Klassen und veranschaulichen, dass sich Änderungen in einer Klasse auf viele andere Klassen auswirken können.
Lesen Sie die Methodenbeschreibungen für jede Operation, um zu verstehen, wie Instanzen der Klasse mit anderen
Objekten kommunizieren und kollaborieren. Wenn ein Objekt eine Nachricht an ein anderes Objekt senden möchte, muss es
eine Referenz auf den Empfänger der Nachricht haben. Ein Kommunikationsdiagramm (eine alternative Darstellung eines
Ablaufdiagramms) zeigt die Objektkommunikation in Form von Verbindungen. Ein Beispiel für ein Kommunikationsdiagramm
sehen Sie in Abbildung 3.
Abbildung 3: Beispiel für ein Kommunikationsdiagramm
Die verbleibenden Nachrichten verwenden entweder eine Assoziation oder eine Aggregation, um die Beziehung
zwischen Instanzen von zwei miteinander kommunizierenden Klassen zu spezifizieren. Informationen zur Auswahl der
angemessenen Darstellung finden Sie im Abschnitt Technik:
Assoziation und im Abschnitt Technik:
Aggregation. Definieren Sie für beide Assoziationen die Sichtbarkeit der Verbindung in Kommunikationsdiagrammen auf
field. Weitere Aufgaben:
-
Konfigurieren Sie die Navigierbarkeit von Assoziationen und Aggregationen. Überlegen Sie hierbei, welche
Navigationsfähigkeiten in den Verbindungsinstanzierungen in den Interaktionsdiagrammen erforderlich sind. Da
Navigierbarkeit standardmäßig eingestellt ist, müssen Sie nur noch die Assoziationen (und Aggregationen) finden, in
denen die einander gegenüberstehenden Verbindungsrollen aller Objekte einer Klasse in der Assoziation keine
Navigierbarkeit erfordern. Inaktivieren Sie in diesen Fällen die Navigierbarkeit für die jeweilige Rolle der
Klasse.
-
Wenn die Assoziation selbst Attribute enthält (dargestellt durch Assoziationsklassen), erstellen Sie eine
Designklasse, die die Assoziationsklasse mit den entsprechenden Attributen darstellt. Fügen Sie diese Klasse
zwischen die beiden anderen Klassen ein und richten Sie Assoziationen mit der entsprechenden Multiplizität zwischen
der Assoziationsklasse und den anderen beiden Klassen ein.
-
Geben Sie an, ob Assoziationsenden geordnet sein müssen oder nicht. Dies ist erforderlich, wenn die
Objekte, die einem Objekt am anderen Ende der Assoziation zugeordnet sind, eine Reihenfolge haben, die eingehalten
werden muss.
-
Wenn die zugeordnete (oder aggregierte) Klasse nur von der aktuellen Klasse referenziert wird, können Sie die
Klasse verschachteln. Die Vorteile der Verschachtelung von Klassen sind eine schnellere Nachrichtenübertragung und
ein einfacheres Designmodell. Verschachtelte Klassen haben jedoch auch Nachteile. Beispielsweise wird der
Speicherplatz für verschachtelte Klassen statisch zugeordnet, selbst wenn es keine Instanzen der verschachtelten
Klasse gibt. Außerdem ist keine Unterscheidung von der übergeordneten Klasse anhand einer Objekt-ID und keine
Referenz auf verschachtelte Klasseninstanzen außerhalb der übergeordneten Klasse möglich.
Assoziationen und Aggregationen lassen sich am besten in einem Klassendiagramm definieren, das die zugeordneten Klassen
veranschaulicht. Eigner des Klassendiagramms muss das Paket sein, das die zugeordneten Klassen enthält.
Abbildung 4 zeigt ein Beispiel für ein Klassendiagramm, das Assoziationen und Aggregationen veranschaulicht.
Abbildung 4: Beispiel für ein Klassendiagram mit Assoziationen, Aggregationen und Generalisierungen zwischen Klassen
Subskriptionsassoziationen zwischen Analyseklassen werden verwendet, um Ereignisabhängigkeiten zwischen Klassen
zu identifizieren. Im Designmodell müssen Sie diese Ereignisabhängigkeiten explizit behandeln, entweder mit verfügbaren
Ereignis-Handler-Frameworks oder durch Entwerfen und Erstellen eines eigenen Ereignis-Handler-Framework. In einigen
Programmiersprachen wie Visual Basic ist dies relativ einfach: Sie deklarieren und behandeln die entsprechenden
Ereignisse. In anderen Sprachen müssen Sie unter Umständen zusätzliche Bibliotheken mit wiederverwendbaren Funktionen
für die Behandlung von Subskriptionen und Ereignissen verwenden. Wenn die Funktionalität nicht gekauft werden kann,
muss sie entworfen und erstellt werden. Weitere Informationen hierzu finden Sie im Abschnitt Technik: Subskriptionsassoziation.
|
Interne Struktur definieren
Einige Klassen stellen komplexe Abstraktionen dar und haben eine komplexe Struktur. Bei der Modellierung einer Klasse
möchte der Designer vielleicht die internen teilnehmenden Elemente und ihre Beziehungen darstellen, um sicherzustellen,
dass der Implementierer die Kollaborationen innerhalb dieser Klasse entsprechend implementiert.
In UML 2.0 sind Klassen als strukturierte Klassen definiert, die eine interne Struktur und Ports haben können.
Klassen können in verbundene Teile zerlegt werden, die dann noch weiter zerlegt werden können. Eine Klasse kann
gekapselt werden, indem eingehende Kommunikation über Ports geleitet werden, die deklarierten Schnittstellen
entsprechen.
Wenn Sie eine komplexe Klasse mit komplexer Struktur finden, erstellen Sie ein zusammengesetztes Strukturdiagramm für
diese Klasse. Modellieren Sie die Teile, die die Rollen für dieses Klassenverhalten übernehmen. Definieren Sie mit
Hilfe von Konnektoren, wie die Teile miteinander verbunden werden. Verwenden Sie Ports mit deklarierten Schnittstellen,
wenn Sie zulassen möchten, dass unterschiedliche Clients dieser Klasse auf bestimmte Teile des Verhaltens dieser Klasse
zugreifen. Verwenden Sie Ports auch, um die internen Teile dieser Klasse von der Umgebung zu isolieren.
Weitere Informationen zu diesem Thema und Beispiele zu zusammengesetzten Strukturdiagrammen finden Sie in Konzept: Strukturierte Klasse.
|
Generalisierungen definieren
Klassen können in einer Generalisierungshierarchie organisiert werden, um allgemeines Verhalten und allgemeine
Strukturen zu verdeutlichen. Es kann eine allgemeine Superklasse definiert werden, von der Unterklassen
Verhalten und Struktur erben können. Generalisierung ist eine Notationserleichterung, die Ihnen ermöglicht, allgemeine
Strukturen und allgemeines Verhalten zentral zu definieren und an den Stellen wiederzuverwenden, an denen dieses
Verhalten und diese Strukturen wieder auftreten. Weitere Informationen zu Generalisierungsbeziehungen finden Sie im
Abschnitt Technik: Generalisierung.
Wenn Sie eine Generalisierung finden, erstellen Sie eine allgemeine Superklasse, die die allgemeinen Attribute,
Assoziationen, Aggregationen und Operationen enthält. Entfernen Sie das allgemeine Verhalten aus den Klassen, die zu
Unterklassen der allgemeinen Superklasse werden. Definieren Sie eine Generalisierungsbeziehung von der
Unterklasse zur Superklasse.
|
Anwendungsfallkollisionen auflösen
Dieser Schritt verhindert Parallelitätskonflikte, die auftreten könnten, wenn zwei oder mehr Anwendungsfälle
gleichzeitig und unter Umständen in unterschiedlicher Weise auf Instanzen der Designklasse zugreifen.
Wenn Sie den Designprozess Anwendungsfall für Anwendungsfall durcharbeiten, besteht eine Schwierigkeit darin, dass zwei
oder mehr Anwendungsfälle versuchen könnten, Operationen in Designobjekten gleichzeitig, aber widersprüchlich
aufzurufen. In diesen Fällen müssen Parallelitätskonflikte explizit identifiziert und gelöst werden.
Wenn Sie mit synchroner Nachrichtenübertragung arbeiten, werden bei der Ausführung einer Operation nachfolgende Aufrufe
der Objekt so lange blockiert, bis die Operation abgeschlossen ist. Eine synchrone Nachrichtenübertragung impliziert
das FIFO-Prinzip bei der Nachrichtenverarbeitung. Dies kann den Parallelitätskonflikt lösen, insbesondere in den
Fällen, in denen alle Nachrichten dieselbe Priorität haben oder alle Nachrichten in demselben Ausführungs-Thread
ausgeführt werden. Wenn ein Objekt von unterschiedlichen Ausführungs-Threads (dargestellt durch aktive Klassen)
aufgerufen werden kann, müssen explizite Mechanismen verwendet werden, um den Parallelitätskonflikt zu verhindern bzw.
zu lösen.
In Echtzeitsystemen, in denen Threads durch Kapseln
dargestellt werden, muss das Problem mehrerer gleichzeitiger Zugriffe auf passive Objekte gelöst werden, wobei die
Kapseln selbst einen Warteschlangenmechanismus unterstützen und eine umfassende Semantik für die Behandlung
gleichzeitiger Zugriffe einsetzen. Es empfiehlt sich, passive Objekte in Kapseln einzubinden. Dadurch wird die
Problematik gleichzeitiger Zugriffe durch die Semantik der Kapsel selbst vermieden.
Es ist möglich, dass mehrere Operationen in demselben Objekt gleichzeitig von unterschiedlichen Ausführungs-Threads
aufgerufen werden, ohne einen Parallelitätskonflikt zu erzeugen. Beispielsweise könnten Name und Adresse eines Kunden
gleichzeitig geändert werden, ohne dass ein Konflikt entsteht. Nur wenn unterschiedliche Ausführungs-Threads versuchen,
dieselbe Eigenschaft des Objekts zu ändern, tritt ein Konflikt auf.
Identifizieren Sie für jedes Objekt, das gleichzeitig von unterschiedlichen Ausführungs-Threads aufgerufen werden kann,
die Codeabschnitte, die vor simultanem Zugriff geschützt werden müssen. In einem frühen Stadium der Ausarbeitungsphase
ist die Identifizierung spezifischer Codesegmente unmöglich. In diesem Fall müssen die zu schützenden Operationen
ausreichen. Bestimmen oder entwerfen Sie anschließend die entsprechenden Steuerungsmechanismen, um konfliktträchtige
simultane Zugriffe zu verhindern. Beispiele für solche Mechanismen sind die Steuerung von Nachrichtenwarteschlangen für
serialisierte Zugriffe, die Verwendung von Semaphoren oder Token, um jeweils nur einem Thread den Zugriff zu erlauben,
und andere Varianten von Sperrmechanismen. Die Auswahl des Mechanismus ist in der Regel implementierungsabhängig und
variiert gewöhnlich mit der Programmiersprache und der Betriebsumgebung. Anleitungen zur Auswahl von
Parallelitätsmechanismen finden Sie in den projektspezifischen Richtlinien.
|
Behandlung nicht funktionaler Anforderungen im Allgemeinen
Die Designklassen werden präzisiert, um allgemeine, nicht funktionale Anforderungen zu behandeln. Eine wichtige Vorgabe
für diesen Schritt sind die nicht funktionalen Anforderungen an eine Analyseklasse, die möglicherweise bereits in den
Sonderanforderungen und Zuständigkeiten der Analyseklasse beschrieben sind. Solche Anforderungen werden häufig mit
Hilfe der Architekturmechanismen (Analysemechanismen) beschrieben, die erforderlich sind, um die Klasse zu realisieren.
In diesem Schritt erfolgt dann die Präzisierung der Klasse und das Einbinden der Designmechanismen für diese
Analysemechanismen.
Die verfügbaren Designmechanismen werden vom Softwarearchitekten identifiziert und charakterisiert. Beschreiben Sie für
jeden erforderlichen Designmechanismus so viele Merkmale wie möglich und geben Sie gegebenenfalls Bereiche an. Weitere
Informationen zu Designmechanismen finden Sie in Aufgabe: Designmechanismen identifizieren, Konzept: Analysemechanismen und Konzept: Design- und Implementierungsmechanismen.
Beim Design von Klassen müssen unter Umständen mehrere allgemeine Designrichtlinien und -mechanismen berücksichtigt
werden. Beispiele:
-
Verwendung vorhandener Produkte und Komponenten
-
Anpassung an die Programmiersprache
-
Verteilung von Objekten
-
Angemessene Leistung
-
Bestimmte Sicherheitsstufen
-
Fehlerbehandlung
|
Ergebnisse auswerten
In diesem Stadium müssen Sie das Designmodell prüfen, um sicherzustellen, dass Sie mit der Arbeit auf dem richtigen Weg
sind. Es ist nicht erforderlich, das Modell im Detail zu prüfen, aber Sie sollten sich die folgenden Prüflisten
ansehen:
|
|