Home

News

Software

Bilder

Texte
  - Börse
  - Favoriten
    - Goddesses
  - Winsock
  - Diplomarbeit
    - Titel
    - Inhalt
    - Einleitung
    - Kapitel 2
      - Kapitel 2-1
      - Kapitel 2-2
      - Kapitel 2-3
    - Kapitel 3
      - Kapitel 3-1
      - Kapitel 3-2
      - Kapitel 3-3
    - Ausblick
    - Literatur
    - Anhang

Alles fliesst

Comics

Musik

Leben

Links

Sitemap

Admin


Kritisches bei
BildBlog

[ TEXT-INDEX] [ 25-stunden-woche] [ Abteilungsbildung] [ Aggression] [ Alleine-im-all] [ Arbeit-im-wertewandel] [ Arbeitsgestaltung] [ Arbeitszufriedenheit] [ Atlantis] [ Betriebssysteme-fakten] [ Betriebssysteme-fragen] [ Betriebssysteme] [ Casetools] [ Client-server-computing] [ Computerknowhow] [ Das-werkzeug-der-mathematik] [ Datenmodellierung] [ Der-gang-ins-all] [ Der-gekruemmte-raum] [ Dialog-arbeitszufriedenheit] [ Eingliederungs-programme] [ Entscheidungstheorie] [ Evolutionaeres-management] [ Familiaer-fraktales-organisationsmodell-1] [ Familiaer-fraktales-organisationsmodell-2] [ Frauenangst] [ Fuehrung] [ Fuzzy-logic-neuronale-netzwerke] [ Ibm-as400] [ Information-retrieval-systeme] [ Informations-management] [ Informations-technik-1] [ Informations-technik-2] [ Ki-mix] [ Leanmanagement] [ Literarischer-stil] [ Mobbing-als-spiel] [ Moderne-arbeitsformen] [ Mythos-ibm] [ Neuronale-netzwerke-mix] [ Objektorientierte-analyse] [ Objektorientierte-entwicklung-cpp] [ Objektorientierte-entwicklung-mix-1] [ Objektorientierte-entwicklung-mix-2] [ Objektorientierte-metriken] [ Objektorientiertes-design] [ Online-transaction-processing] [ Organisationsanalyse] [ Organisationsentwicklung] [ Organisationskultur] [ Organisationspsychologie1] [ Organisationspsychologie2] [ Organisationsstrukturen] [ Organisationstheorie1] [ Organisationstheorie2] [ Organisatorische-effizienz] [ Organisatorische-fuehrung] [ Outsourcing] [ Papierkorbmodell] [ Pc-host-kommunikation] [ Personalbeurteilung] [ Perspektive-in-der-geschichte] [ Philosophie-der-neuzeit] [ Philosophie-des-mittelalters] [ Property-rights-theorie] [ Rationalisierung-in-der-verwaltung] [ Rechnernetze-fragen] [ Rechnernetze-muendlich] [ Rechnernetze] [ Restrukturierung] [ Revitalisierung] [ Simulation-neuronaler-netzwerke] [ Sw-hw-migration] [ Sw-reuse] [ Transzendenz-kunst-religion] [ Und-ward-nicht-mehr-gesehen] [ Verteilte-betriebssysteme] [ Wissensbasierte-systeme] [ Wissenschaftstheorie] [ Wut-und-ihr-nutzen]


Objektorientierte Entwicklung (OOE): Splitter I

von Daniel Schwamm (04.07.1994 bis 01.08.1994)

Aus "Heimat des Dilettantismus"
http://www.henrys.de/daniel/index.php?cmd=texte_objektorientierte-entwicklung-mix-1.htm
nach Coad/Yourdon, Coad/Nicola, Scott Meyer, Schader/Kuhlins (1990 bis 1994)

* Definitionen: Allokieren für Objekte den Speicherplatz und beinhalten für Funktionen den Funktionsrumpf. Objekte und Funktionen (außer inline-Funktionen) werden nur einmal pro Programm definiert.

* Default-Konstruktor: Ein Konstruktor, den man ohne Parameter anzugeben aufrufen kann. Entweder erlaubt er keine Parameter ("Konstruktor();") oder die Parameter führen Default-Werte ("Konstruktor(int i=10);"). Auch Konstruktoren, die der Compiler automatisch generiert, sind Default-Konstruktoren.

* Der Präprozessor wurde bei C intensiv eingesetzt, sollte jedoch bei C++ vermieden werden. Statt "#define" sollte mit "const" und "inline" gearbeitet werden. Makros lassen sich durch Templates simulieren. Beispiel:

template<class T> // Achtung! Kein ";" dahinter!

inline T& MAX(T &a, T &b) {

return a>b?a:b;

};

* Die "stdio.h"-Bibliothek sollte durch die "iostream.h"-Bibliothek ersetzt werden, da letztere die Ausgabe von benutzerdefinierte Typen erlaubt.

* "operator<<" sollte in Klassen als "friend" aufgenommen werden. Dann kann dieser überladene Operator in herkömmlicher Weise mit benutzerdefinierten Typen eingesetzt werden. Implementierung:

friend ostream& operator<<(ostream &s, const classtype &t) {

s << t.daten;

return s;

};

Achtung! "ostream" darf nicht "const" sein. Überall muß referenziert werden, bis auf "classtype". Dort kann auch gepointert oder mit Temporärobjekten gearbeitet werden.

* HIC heißt Human Interaction Class. Sie wird im OOD-Prozeß modelliert, u.a. getrennt von der Problem Domain Class (PDC).

* Bei der Analyse der Problembereichs-Komponente sollten bereits möglichst allgemeine Klassennamen gebildet werden, um SW-Reuse zu ermöglichen.

* Coad/Nicola stellen das Baseball-Modell vor: Zuerst macht man OOA, dann OOD (z.B. die PDC), dann OOP (Implementierung von PDC als Prototyp), dann wieder OOD (diesemal die HIC), dann OOP, dann OOD (Task Management Class; TMC), dann OOP und dann OOD (Data Management Class; DMC). Schließlich kann wieder mit OOA fortgefahren werden und die Spirale des Baseball-Modells wiederholt sich.

* Referenzattribute, häufig Surrogate, sollten im OOD-Modell (noch nicht im OOA-Modell) mit hintenanstehendem "(d)" für Design gekennzeichnet werden. Das Attribut selbst trägt den Namen der referenzierten Klassen (möglicherweise allerdings im Plural). Referenzen in einem Objekt sind immer dann nötig, wenn dem referenzierten Objekt mindestens eine Nachricht zukommt, was durch einen Nachrichtenpfeil gekennzeichnet wird.

* Wichtige Überladungsoperatoren sind z.B. "operator=" für Zuweisungen (Konversionsfunktion) und "operator==" für Objektevergleiche.

* Es ist oft sinnvoll, C-Basistypen wie z.B. "char*" durch benutzerdefinierte Typen wie z.B. "String" zu ersetzten, da die benutzerdefinierten Typen extern, d.h. individuell vom Programmierer, gewartet und erweitert bzw. beschränkt werden können.

* Für Deep-Copy (Copy, bei dem auch Pointer mitkopiert werden) werden Copy-Konstruktoren in den Klassen benötigt. Der Default-Copy-Konstruktor kopiert nur die Ausprägungen der C-basierten Typen, sofern sie keine Pointer sind. Allerdings: Auch Zuweisungs-Kontruktoren können für Deep-Copy benutzt werden. Beispiel:

class X {

char *sp;

X(X& x) { // Achtung! Kein X(X x);

sp=x.sp; // (weil sonst Ewigrekursion)

return *this; // dadurch a=b=c möglich!

};

* OOE-Leute sollten sich an das "Continuum of Representation"-Prinzip halten, d.h. ein durchgängiges Modell für die OOE benutzten, z.B. das OOE-Modell von Coad/Yourdon. OOA, OOD und OOP können unabhängig voneinander gewartet werden, ohne daß dabei Inkonsistenzen auftreten müssen. Die Phasen sind jederzeit untereinander transferierbar.

* HIC-Implementationen sollten so früh als möglich als rudimentäre Version realisiert werden, um dem User ein Bild des späteren Produkts zu geben (Prototyping-Prinzip).

* Es hilft beim analysieren und designen, wenn man sich an das "I'm alife"-Prinzip hält, d.h. über Objekte in der Ich-Form nachdenkt, z.B. "Ich bin ein Schalter. Wenn x passiert, dann mache ich y und z".

* Sehr ähnliche Klassen sollten vermieden werden. Man sollte versuchen, ob die Klasse nicht besser durch Aufnahme einer Funktion in die ähnliche Klasse transferierbar ist. Beispiel: Es hat wenig Sinn, für eine Zählerklasse für jede Basis eine eigene abgeleitete Zählerklasse zu entwickeln, da eine allgemeine Transformation der Basen durch eine Methode realisiert werden kann.

* OOE wird in Projekten betrieben. 12 Mann gelten hier als Richtlinie für größere Projekte, wobei je nach Fähigkeit die Teilnehmer auf die Phasen OOA, OOD und OOP verteilt werden, wo sie dann nach dem Baseball-Modell in gegenseitiger Konkurrenz zueinander ihren Teil der OOE abarbeiten können.

* Die Klassen PDC, HIC, TMC und DMC des Designs sollten getrennt gehalten werden, d.h. nur über Beziehungen und Nachrichtenaustausch zueinander im Kontakt stehen, niemals aber über Vererbungs- oder Kontainer-Strukturen. Dadurch wirken sich Änderungen in einer Klasse nicht auf die anderen Klassen aus (niedriges Coupling).

* Werden bei der HIC Fensterklassen gebildet, sollten deren Namen ihre Ausgabe wiedergeben, z.B. "Number", nicht ihre Funktion, z.B. "Counter of Terms". Dies ist wichtig im Bezug auf SW-Reuse.

* Die HIC sollte sich soweit wie möglich die Klassen des GUIs zunutze machen, z.B. direkt einsetzen oder eigene Klassen davon ableiten.

* Zusammengesetzte Klassennamen wie z.B. "Segelschiff" deuten oft an, daß die Klasse aufspaltbar ist, hier z.B. in die "is a"-Struktur "Schiff" und "Segelschiff".

* Zu beachten: "class A{A();}; A *a=new A[10];" ruft insgesamt 10mal den Default-Konstruktor "A()" auf! Einen anderen Kontruktor als einen Default-Konstruktor einzusetzen, geht in diesem Falle nicht. Man sieht also, daß man u.U. auf einen Default-Konstruktor nicht verzichten kann.

* Ein Copy-Konstruktor sieht folgendermaßen aus: "String(const String &s);" Auf das "const" kann verzichtet werden, nicht aber auf das Referenzzeichen (Gefahr der Ewigrekursion)! Dieser Copy-Konstruktor kann auf zwei Arten eingesetzt werden: "String s1(s2)" oder "String s1=s2". I.d.R. übernimmt der (Copy-)Konstruktor eine Überprüfung der Gültigkeit, im Ggs. zum Zuweisungsoperator. Wird der Zuweisungsoperator überladen, wird statt des Copy-Konstruktors bei Operationen mit "="-Verwendung immer der Zuweisungsoperator verwendet!

* Der Zuweisungsoperator, der im Ggs. zum Copy-Konstruktor keine Gültigkeitskontrolle betreibt, kann folgendermaßen aufgebaut werden:

String& String::operator=(const String &s){

if(&s==this) return *this;

daten=s.daten; // Deep-Copy möglich!

return *this; // Für a=b=c!

};

* Es gilt: a+b <==> operator+(a, b); Wird der "+"-Operator überladen, so sollte auch der "++" und +="-Operator überladen werden. V.a. mit dem "++"-Operator gibt es Probleme, weil der Präfix oder Postfix verwendet werden kann. Postfix erhält i.d.R. ein Dummy-"int" als Argument (funktioniert bei Borland C++ 2.0 nicht!), um ihn vom Präfix-Inkrementierer unterscheiden zu können. Beispiel für den interessanteren Postfix-Inkrementierer:

X X::operator++(int) { // int steht für Postfix!

X *temp=*this; // aktuellen Zustand retten

i++; // Zustand inkrementieren

j++;

return temp; // voraktulenn Zustand zurück

};

* Konventionen für Variablennamen: "p" für Pointer, "r" für Referenzen, "s" für statische Elemente oder Strings, "ch" für Charakter, Großbuchstaben für geconstete Variablen.

* "new" und "delete" sind "malloc" und "free" vorzuziehen, da letztere keinen Aufruf des Konstruktors bzw. Destruktors impliziert haben.

* Statische Elemente von Klassen sind außerhalb der Klasse zu definieren.

class X { static int sx; static char sy; };

int X::sx; // Initiierung mit Null

char X::sy='a'; // Initiierung mit 'a'

* "friends" befinden sich zwar innerhalb von Klassen, werden jedoch nicht zu den Elementfunktionen dazugezählt!

* Template-Funktionen innerhalb von Nicht-Template-Klassen sind verboten!

* Ein einfaches "::new" sorgt dafür, daß alle klassenspezifischen "news" umgangen werden und die globale "new"-Funktion aktiviert wird.

* Referenzenzählen (!=Objektzählen): Buchführung darüber, wieviele Objekte aktuell auf eine bestimmte Datenstruktur zeigen. Dadurch weiß man z.B., ob die Datenressource noch benötigt wird oder aus dem Speicher entfernt werden kann.

* Konstruktoren sollten so aufgebaut sein, daß Initialisierungen über den Funktionskopf den Zuweisungen im Funktionsrumpf vorgezogen werden. Bei der Initialisierung von "const"- und "Referenz"-Variablen ist man sogar auf die Initialisierungsliste des Konstruktors angewiesen. Beispiel:

struct X {

const char *s;

X(const char *sp) { s=sp; } // ERROR

X(const char *sp):s(sp) {}; // OK

};

* Objektzählen-Implementation:

struct X {

static int z;

X() { z++};

~X() { z--};

};

int X::z; // Initiierung mit Null.

* Destruktoren von Basisklassen sollten unbedingt virtuell deklariert werden. Warum? Damit über einen "basierten" Zeiger eine Destruktion des abgeleiteten Objekts auch alle Basisklassen-Destruktoren aktiviert (die im Ggs. zu allen anderen virtuellen Funktionen nicht in jeder Klasse die gleiche Bezeichnung besitzen müssen). Nur so wird das Objekt vollständig aus dem Speicher geräumt.

* Bei Klassen, die nicht als Basisklassen dienen sollen, sollte auf "virtual"-Deklarationen verzichtet werden, denn der virtuelle Mechanismus verlangt Zusatzaufwand. So benötigt er z.B. pro Objekt einen "Virtual Table Pointer", der auf die Zusatzstruktur "Virtual Table" zeigt, die pro Klasse genau einmal angelegt wird, und die die Adressen der virtuellen Funktionen in Abhängigkeit vom Objekt-Aufrufer führt.

* Der Zuweisungsoperator "operator=" (wie überhaupt viele Operatoren) sollte eine Referenz auf "this" zurückliefern, damit Kettenzuweisungen der Form "a=b=c" möglich sind. Implementierung:

String& operator=(String *sp) { ... return *this; };

* Der Hauptunterschied zwischen globalen Funktionen und Elementfunktionen ist der, daß nur letztere virtuell sein können.

* Wird der Operator "operator<<" der "iostream.h"-Bibliothek global für eine benutzerdefinierte Ausgabe überschrieben, erhält er u.U. keinen Zugriff auf alle Klassenelemente (z.B. PRIVATE/PROTECTED-Elemente). Wird er als Elementfunktion überschrieben, sind auch Ausgaben der Form "Variable << cout" möglich, was aber nicht erwünscht ist, da es von der Norm abweicht. Aus diesem Grund ist dieser Operator als "friend" der Klasse zu deklarieren.

* Beim Design von Klassen sollte man sich an das Prinzip der funktionellen Abstraktion halten, d.h. Klassenlemente PRIVATE bzw. PROTECTED gestalten und für den Anwender evtl. Funktionen zur Manipulation bereitstellen.

* Einen Zeiger auf eine Datenstruktur, die man nicht verändern soll, erhält man z.B. durch "const char *sp". Der Zeiger "sp" bleibt dabei aber veränderlich, kann also z.B. um einen Buchstaben hochgesetzt werden ("sp=&sp[1];"). Möchte man auch den Zeiger unveränderlich halten, ist ein weiteres "const" in den Term einzufügen: "const char* const sp". Man beachte dabei die Position von "*"! Möchte man nur den Pointer konstant halten, den Datenteil aber veränderlich halten, so eignet sich hierfür "char * const sp".

* Es gilt: Elementfunktionen, die sich nur hinsichtlich ihrer "const"-Eigenschaften unterscheiden (Rückgabewert oder Parameterliste), können ohne Parameter-Matching-Probleme überladen werden.

* In C wurde die Übergabe via Wert bevorzugt, und auch in C++ ist sie Standard. Doch eine Übergabe via Referenz ist wesentlich effektiver, sofern nicht nur sehr kleine Variablen wie z.B. "shorts" (1 Byte) übergeben werden, denn Pointer benötigen 4 Bytes. Die Effizienz rührt v.a. daher, daß Refrenzenaufrufe keine Konstruktoren und Destruktoren bemühen müssen, um temporäre Objekte anzulegen.

* Die "const"-Eigenschaft läßt sich "weg-casten". Das ist manchmal notwendig, wenn Funktionen nicht-konstante Variablen wünschen, wo eigentlich konstante Objekte die Regel sind. Beispiel: Die "strlen"-Funktion (zählt übrigens den Terminator NICHT mit!) verlangt einen nicht-konstanten Parameter (in UNIX, nicht bei Borland C++ 2.0). Ein "weg-casten" der "const"-Eigenschaft läßt sich erreichen über:

const char *sp="test";

int i=strlen((char *)sp);

* Beim Konvertieren eines Objektes auf seine Basisklasse muß auf das Slicing-Problem geachtet werden: Im Ggs. zu den Upcasts von Pointern verlieren "basierte" Objekte (nicht Pointer!) ihre Fähigkeiten, die sie als abgeleitete Objekte noch inne hatten; sie werden zu normalen Basisklassen-Objekte, der Virtual-Mechanismus ist lahm gelegt.

struct X { virtual f(); };

struct Y:public virtual X { f(); };

Y *yp=new Y;

Y y;

X *xp=&y;

X x=y; // Object-Upcasting!

xp->f(); // ruft virtuell Y::f() auf.

x.f(); // ruft (fälschlicherweise) X::f() auf.

* Der Opertor "operator+" verlangt eine Übergabe via Wert, da er ein neues Objekt erzeugen muß.

* Überladen funktioniert nicht immer, so gelingt z.B. bei Null-Werten kein Parameter-Matching, da nicht zwischen "char", "int" und Pointern unterschieden werden kann. Bisweilen ist auch ein Parameter-Defaulting der bessere Weg, wobei aber sinnvolle Default-Werte existieren sollten.

* Templates haben nur globalen Sicherheitsbereich, d.h. eine Typkonversion ist nur innerhalb einer Datei möglich, ein Programm kann aber aus vielen inkludierten Dateien bestehen.

* Achtung! Ambiguitäten (Uneindeutigkeiten) können dem Compiler entgehen, z.B. bei mehrfacher Vererbung (z.B. zwei Basisklassen füllen eine abgeleitete Klasse mit Eigenschaften), sofern die Basisklassen gleichnamige Funktionen bzw. Attribute haben.

* Wie kann man verhindern, daß sich Anwender Kopien von Objekten erzeugen können? Durch Weglassen eines Copy-Konstruktors gelingt das nur zum Teil, denn ein Soft-Copy wird durch einen automatisierten Copy-Konstruktor gegeben. Wie verhindere ich diesen automatisierten Copy-Konstruktor. Indem ich einen Copy-Konstruktor vorgebe, diesen aber nur deklariere und nicht definiere, und ihn am besten im PRIVATE-Teil ablege. Versucht ein Anwender nun eine Zuweisung, so wird sich entweder der Linker oder der Compiler beschweren.

* Bei größeren Programmen kann es durch inkludierte Dateien zu Namenskonflikten kommen. Designer von Bibliotheken können diesen Effekt mildern, indem sie ihre Variablen in "structs" als "static"-Variablen deklarieren und außerhalb der "structs" definieren. Der Anwender muß dann über ein vorangestelltes "Struct-Name::" auf diese Variablen zugreifen. Falls keine Namenskonflikte zu erwarten sind, kann man ein Header-File mit lauter "typdef Varname Struct-Name::Varname;" bilden, das sich der Anwender inkludieren kann.

* Die 80-20-Regel besagt, daß 80% der Zeit nur 20% des Codes genutzt wird. Nur diese 20% des Codes sind haupsächlich zu optimieren.

* Öffentliche Vererblichkeit (über PUBLIC) bedeutet eine "is a"-Vererbung. Wird diese virtuell realisiert, dann wird nur die Schnittstelle vererbt, nicht aber die Implementationen; diese sind in der abgeleiteten Klasse neu zu definieren. Anders bei nicht-virtueller Vererbung: Hier wird die Schnittstelle samt ihren Implementationen vererbt; diese sollten nicht überschrieben werden.

* "pure virtual"-Funktionen sind definierbar (!), sie müssen allerdings beim Aufruf vollständig qualifiziert werden (kein Paramater-Defaulting).

* Nicht-virtuelle Klassen sind im Ggs. zu virtuellen Klassen nicht dynamisch gebunden, sondern statisch. Schon vor Laufzeit muß feststehen, von welchem Objekttyp sie aufgerufen werden, daher sollten ihre Funktionen nicht überladen werden, den "basierte" Pointer können nicht virtuell arbeiten, d.h. die abgeleitete Funktion aufrufen (in Borland C++ 2.0 schon: Auf Klassen-"virtual" kann verzichtet werden, nicht jedoch auf das Funktionen-"virtual").

* Default-Parameter einer virtuellen Funktion sollten in der abgeleiteten Klasse nie geändert werden, denn Default-Parameter sind immer statisch gebunden, d.h. "basierte" Pointer rufen immer nur die Basis-Defaults auf, nie die abgeleiteten!

* Alle Datenstrukturen, die gebräuchlich sind, sind potentielle Kandidaten für das Templating. Spezialisierung ist nur nötig, falls die Typen die Eigenschaften der Klasse beeinflussen, z.B. andere (adaptive) Funktionen erfordern.

* Downcasts sind Casts, bei denen ein Basisklassen-Pointer auf abgeleitete Klassen konvertiert wird. Das ist eine unsichere Sache, da nicht definiert ist, ob der gedowncastete Pointer erlaubt, auf Funktionen der abgeleiteten Klasse zuzugreifen (wird erst während der Laufzeit festgestellt). Wann immer möglich, sollte auf Downcasts verzichtet werden. Bei virtueller Klassen-Vererbung sind Downcast sogar explizit verboten (Borland C++ 2.0 bricht die Compilierung ab, ohne den Fehler zu benennen)! Beispiel:

class X{ int virtual f(); }; // Funktions-"virtual" geht!

class Y:public X{ int i; Y(int i){i=1;}; int f(); };

X *x=new X;

x->f(); // OK

((Y*)x)->f(); // Downcast: Konstruktor wird nicht

// aufgerufen. Daher führt "i" Zufallswert!

((Y)x).f(); // Downcast: Der Compiler verlangt einen

// Copy-Konstruktor!

* "has a"- und "is implemented with"-Strukturen sind durch Layering implementierbar, d.h. durch einen Pointer in der Basisklasse auf ein Feld von Teileobjekten (klassenwertiges Attribut) .

* "is implemented with" kann durch Layering realisiert werden oder im notfall auch durch PRIVATE-Vererbung (eine PROTECETED-Ableitung ist nicht möglich). Dadurch kann man Eigenschaften von Basisklassen gewinnen, ohne dadurch eine Beziehung zu modellieren, da dieser Code nur klassenintern nutzbar ist und nicht für Anwender zugreifbar ist (private Erblichkeit). Beispiel:

class X{public: int f();};

class Y:private X{public: int g();};

Y *y=new Y;

y->f(); // ZUGRIFFSERROR, obwohl f() PUBLIC ist!

* Globale Objekte und "static"-Variablen sollten vor "main()" initialisiert sein, ansonsten setzt sie der Compiler automatisch auf Null (daher bedeutet "int i;" eine Deklaration UND eine Definition!). Die Reihenfolge der realen Initialisierung hängt einzig und alleine von der Reihenfolge der Deklarationen ab, nicht von der Reihenfolge der Initialisierungen!

* Achtung! Statische Elemente und Funktionen verhalten sich wie Nicht-Objekte, d.h. sie lassen sich nicht refrenzieren wie die gewöhnlichen Objekte!

* Ein Prefix-Inkrement (++i) verlangt den Operator "operator++()". Ein Postfix-Dekrement (i--) den Operator "operator--(int)".

* Beispiel für die Entwicklung einer Daten-Management-Komponente:

I. Szenerarien in "I'm alife"-Form:

(1) Ich bin eine ObjectTable: Um ein Objekt zu laden, öffne ich die Objektedatei, lade diverse Variablen, kreiere alle zum Objekt gehörenden PDCObjects und sage den PDCObjects, sie sollen sich selbst laden. Danach schließe ich die Objektdatei wieder.

(2) Ich bin ein PDCObject. Wenn ich mich selbst laden soll, dann sage ich meinem korrespondierenden PDCObjectTagFormat, es soll sich selbst laden.

(3) Ich bin ein PDCObjectTagFormat. Ich lade mich, indem ich meine Attribute, meine Objekt-ID und meine Beziehungen einlese.

II. OOA-/OOD-Modell der DMC (plus angedeutete PDC):

ObjectTable ObjectTagFormat

PDCObjects (d) ObjectID

storeObjects storeAttributes

restoreObjects restoreAttributes

storeObjectID

...

 

 

 

PDCObject PDCObjectTagFormat

ObjectID (d)

storeUsingFormat store

restoreUsingFormat restore

 

 

 

 

Customer Product CustomerFormat ProductFormat

Name Name

Adress Price

storeAttributes storeAttr.

restoreAttributes restoreAttr.

... ...

 

* Nimmt ein Objekt Kontakt auf zu einem Objekt der gleichen Klasse, wird dies dadurch modelliert, daß eine Beziehung plus ein Nachrichtenpfeil links/rechts aus dem Objektsymbol austritt und links/rechts wieder in dasselbe eintritt.

* Während beim OOA-Modell noch alle Kardinalitäten angegeben werden, könne sich diese im OOD-Modell auf diejenigen reduzieren, die unbedingt nötig sind. Wenn ein Objekt einem anderen Objekt eine Nachricht zukommen läßt, daß andere Objekt dies aber nicht tut (bis auf die Rückantwort, die aber nicht durch einen Nachrichtenpfeil angedeutet wird), dann genügt eine Kardinalität auf der Seite des Sendeobjekts.

* Es gilt: x->f(); <==> (*x).f();

* Die Kardinalität bei einem Objekt gibt an, wie oft es eine Beziehung mit dem gegenüberliegendem Objekt eingeht. Bei Aggregations-/Zerlegungs-Strukturen gilt das gleiche: Die Kardinalität beim Aggregat gibt an, aus wievielen der Zerlegungsobjekten es besteht (meistens "0, m", während die Kardinalität der Zerlegung angibt, wieviel Aggregaten eines davon angehören kann (meisten "1").

* Beziehungen werden durch einfache Linien symbolisiert, die seitwärts aus den Objektsymbolen führen. Die Pfeile der Nachrichten werden ebenfalls seitwärts modelliert. Aggregations-/Zerlegungsstrukturen (eine Sonderform von Mehrfachbeziehungen) verlaufen wie die "is a"-Vererbungsstruktur von oben nach unten.

* Beispiel für die Modellierung einer HIC:

ModelViewContainer |

|

layout HIC | PDC

|

|

|

DisplayBox Button | XYZ

|

action |

label |

|

displayValue displayLabel |

update push |

 

* ALGOL-60/68 (1960) war ein C++/Pascal-Vorläufer. Daher kommen Blöcke, Prozesse, Rekursion, Operatoren-Überladung.

* Der Adreßoperator wird auch &-Operator oder Referenz-Operator genannt.

* Es gibt kein Array mit Referenz-Elementen (aber Pointer-Elementen). Es gibt auch kein Array mit Funktionen, aber mit Funktionspointern!

* Die Assoziativität gibt Auskunft über die Operator-Abarbeitungs-Reihenfolge. Rechts-assoziativ sind der Zuweisungs-Operator und alle UNÄREN Operationen (z.B. int **a <=> int *(*a)). Links-assoziativ ist der Rest (z.B. a && b && c => (a && b ) && c).

* Aufzählungen (Enumerations) DEKLARIEREN (keine Speicherplatz-Allokation) eine Menge GANZzahliger KONSTANTEN. Z.B. enum Tag { übeltag, dienstag=1, mittwoch }.

* Die Fehler-Ausnahmebehandlung sieht folgendes vor:

Im Code:

if( BestimmterFehler==TRUE ) throw Fehlertyp();

Fehlerbehandlungsroutine:

try { vielleicht Fehler }

catch (Fehlertyp1) {ja, Fehler 1}

catch (Fehlertyp2) {...}

};

* Klassen können direkte oder indirekte Basisklassen sein.

* Die Bibliothek enthält eine Menge von compilierten Objekt-Klassen, deren Deklarationen in Header-Dateien stehen. Der Linker liest nur die benötigten Bibliotheks-Teile ein.

* Die Bindung gibt den Geltungsbereich von Variablen vor. Globale Namen gelten in jeder Datei eines Programms (externe Bindung), außer sie wurden mit static, const (ohne extern davor) oder inline spezifiziert (dann interne Bindung). "typedef"-Anweisungen und Enumeratoren haben immer interne, Klassen dagegen fast immer externe Bindung.

* Ein 8-Bit-Feld läßt sich folgendermaßen implementieren:

struct X {

unsigned int bit1:1;

...;

unsigned int lastbit:8;

};

Ein Zugriff ist mittels "X x; if(x.bit1){}" möglich. Zeiger bzw. Referenzen sind nicht möglich, dafür aber Manipulation mit "->" statt mit den "<<"- oder ">>"-Operatoren.

* Ein Builder ist ein Prg., welches Modelle (z.B. ERM, X11-Widgets) in Codeteile überführt (zB. C++-Objects) und fertige Applikationen daraus bildet. +: Änderungen am Modell bewirken dynamische Änderung am Code.

* C von Kernighan/Ritchie ist eine prozedurale Sprache, die noch nicht Klassen, new, delete, throw, try kennt. C++ 1980 maßgeblich von Stroustrup entwickelt (ohne High-Level-Typen).

* Array-Übergaben sind immer Call by Reference.

* Containerklassen werden i.d.R. über Templates realisiert.

* Die Dereferenzierung geschieht mittels des Inhaltsoperators "operator*", um den Wert, auf den Typ zeigt, zu liefern. Achtung! Funktioniert nicht mit void-Zeigern!

* Destruktoren werden auch Cleanups genannt. Wenn sie "virtual" sind, kann eine abgeleitete Klasse auch AUTOMATISCH die Basisklassen löschen.

* Der Ellipseoperator "(...)" ist für die Argument-Übergabe nützlich, wenn die Anzahl der übergebenen Argumente variieren kann oder unbekannt ist. Beispiel:

f( int a, ... ){};

f(1); // OK

f(1, "Text", 22 ); // OK

* Die Escape-Sequenz wird benutzt zum Ausdruck nicht-ausdruckbarer Zeichen: Z.B. '\?', '\t' oder '\ooo' für Oktalziffern, '\xhhh' für Hexadezimalziffern.

* Feldernamen (z.B. int i[]={1,2}) sind nicht mit Zeigern identisch, da sie unveränderbar sind (keine L-Values!) und Speicherplatz reservieren.

* Das Argument-Matching erfolgt über folgende Stufen:

(1) triviale Konversion (T&->T).

(2) Typangleichung (char->int).

(3) Standardkonversion (int->long->const long).

(4) benutzerdefinierte Konversion.

* Der Funktionsoperator "()" wird auch cast-Operator genannt. Er muß eine Elementfunktion sein. Günstig für die Parameterübergabe "X x=y(1,2)".

* Die Headerdateien enhalten nur EXTERN-DEKLARATIONEN (die Definitionen liegen in den ".cpp"-Dateien). Sie enthalten nicht: Funktionsdefinitionen, Datendefinitionen (wie int i;) oder Aggregate-Initialsierungen der Form "t[]={...}".

* Es gilt: a---b <=> a-- - b (-- bindet stärker als -). ++++a (Achtung! "+" Vielfache von 2!) geht, aber a---- geht wegen der Links-Assoziativität nicht. Ebenso führt ++i++ zu einem L-Value-Error.

* Konstanten können literal (selbstsprechend) sein, z.B. 143=0217=0x8f=0x8F oder '\"Zeichenkette\"' oder ""+Terminator oder .478e2. Sie sind symbolisch, wenn ein "const" vor dem Bezeichner steht.

* Der Konstruktor kann nicht virtual, const, volatile oder static sein.

* Der Polymorphismus bietet generische Funktionen, d.h. syntaktisch gleiche Namen rufen semantisch ähnliche Methoden auf, wodurch insgesamt weniger Nachrichtennamen im System existieren.

* Smart-Zeiger-Klassen sind Klassen, bei denen der "operator->" überladen wird. Dadurch können bei jeder "->"-Anwendung irgendwelche Funktionen ausgelöst werden.

* Die Speicherbelegung von C++-Programmen sieht folgendermaßen aus:

- Code-Teil (fixiert): Enthält die Funktionen-Implementationen.

- Daten-Teil (fixiert): Enthält globale und static-Objekte.

- Stack (variabel): Enhält lokale Variablen während Blockaktivität.

- Heap=Freispeicher (variabel): Enhält die new- und delete-Objekte.

* Überladen ist eine Polymorphismus-Form, Parameter-Defaulting eine andere. Nicht überladbar sind die Operatoren: sizeof, ., .*, ::, ?:. Eine weitere Polymorphismus-Form stellt schließlich noch das OBJEKTABHÄNGIGE Funktions-Auswahl-Verhalten dar.

* L(ocations)-Values beschreiben die Adresse einer Variablen (diese ist nicht änderbar). R(ead)-Values beschreiben den änderbaren Wert einer Variablen.

* "inline" und "virtual" verträgt sich nicht zusammen!

* Folgender Fall ist unmöglich, aber durch anschließende substituierbar:

class X { X x; }; // ERROR: Compiler weiß Größe von X nicht!

class X { X *x; }; // OK: Compiler kennt Pointer-Größe!

class X { X &x; }; // OK: Compiler kennt Referenzgröße!

* Der Zuweisungsoperator "=" wird im Ggs. zu allen anderen Operatoren NICHT VERERBT!

* Abstrakte Basisklassen benötigen mindestens eine "pure virtual"-Elementfunktion!

* Wegen ihrer Ähnlichkeit sind folgende Funktionen nicht überladbar:

- f(int&i) versus f(int i)

- f(int[]) versus f(int*)

- f(char*) versus f(char*const)

* Trotz ihrer Ähnlichkeit sind folgende Funktionen überladbar:

- f(int[][10]) versus f(int[][3])

- f(const int*) versus f(int*)

- f()const versus f()

* Das Schlüsselwort "extern" erlaubt die mehrfache DEKLARATION eines Typs, z.B. extern int a; (KEINE DEFINITION wie int a;). Der Typ muß und darf aber in einer Datei nur genau einmal definiert sein.

* "friend"-Deklarationen sind NICHT TRANSITIV! Außerdem besitzen sie keinen IMPLIZITEN THIS-Zeiger, müssen also einen Pointer/eine Referenz als Argument führen.

* Mehrdimensionales Arrays können folgendermaßen aufgebaut werden:

X x [a] [b]; // Array von a Arrays mit jeweils b X-Elementen.

int a[][2]={ {1,2}, {3,4}, {5,6} };

Die Größe läßt sich über "a*b*sizeof(X);" oder "sizeof(x);" ermitteln.

* "friend" können in Template-Klassen benutzt werden, entweder in parametisierter Form (Klassen-Typ oder Funktionstyp möglich) oder in nicht-parametisierter Form.

* "static" hat zwei Bedeutungen: Innerhalb von Klassen bedeuten "static"-Deklarationen, daß die Variabel/Funktion von allen Instanzen gemeinsam genutzt wird und außerhalb der Klasse über "TYP Klasse::Varibale=x;" zu initialisieren ist. "static"-Elementfunktionen besitzen keinen impliziten "this"-Pointer! Für "static"-Deklarationen außerhalb von Klassen gilt, daß diese Variablen/Funktionen nur INNERHALB eine Datei erkannt werden (und nicht im ganzen Programm).

* Der "union"-Befehl kann zum Speicherplatzsparen verwendet werden: Mehrere Variablen teilen sich hiermit einen Speicherbereich. Beispiel:

struct X {

union {

char b;

int j;

}; // nur b oder j führt einen Wert, nie beide!

};

* Virtuelle Destruktoren müssen im Ggs. zu allen anderen virtuellen Funktionen NICHT den Bezeichner für die abgeleitete Klassen vorgeben, was aber auch logisch ist, da der Destruktor immer genauso wie die Klasse heißen muß, die ihn besitzt (mit vorangestellter Tilde).

* Bei "void"-Typen erlaubt C++ keine Zeigerarithmetik der Form "void *xp; xp++;", da der Compiler die Größe von "void" nicht kennt!

* Eine reine Protokollklasse ist eine Klasse ohne Attribute; sie ist auch immer abstrakt.

* Aggregationsstrukturen werden bei der OOA ohne klassenwertige Attribute bzw. Referenzen modelliert. Die Technikdetaills werden erst ins OOD eingebaut. Anders sieht es bei den Kardinalitäten aus: Beim OOD-Modell kann auf dieselben verzichtet werden, wenn ein Teilobjekt nicht wissen muß, zu welcher Gesamtheit es gehört.

* (Broadcast-)Nachrichtenverbindungen zwischen Objekten benötigen Beziehungen oder eine Aggregationsstruktur. Verbindungen bis an die Klasse bedeuten, daß immer ein Konstruktoraufrufe dabei ist. Einzige Ausnahme: Polymorphe Aufrufe von abstrakten Klassen-Funktionen.

* Checkliste zum finden von Objekten: Rollen, Orte, Externes (aber zur Systemverantwortung gehörendes), Interaktionen (z.B. Vertrag), Ereignisse, Beschreibungen (z.B. Rezeptur) und Organisationseinheiten.

* Ableitbare Informationen (aus Performancegründen) sollten erst im OOD-Modell modelliert werden. Auch findet dort mit unter eine OOA-Attribute-Aufschlüssel statt, z.B. Anschrift ergibt Name, Ort, PLZ, ...

* Für Vererbungsstrukturen gilt die Regel: Attribute so hoch wie möglich (==> Redundanz-) und so tief wie nötig (==> kein Informations-Overloading) anordnen!

* Sind Beziehung temporärer Natur, z.B. Bus<->Fahrer, so ist es häufig sinnvoll, Ereignisklassen dazwischen einzufügen, z.B. Fahrt(Datum). Eine solche Ereignisklasse eignet sich auch dann, wenn mehrere Arten von Beziehungen zwischen Objekten vorliegen können, z.B. "Planung" und "Aktuell". Die Beziehungen werden in der OOA-Attributeschicht modelliert, weil im OOD entsprechende Attribute zur Erreichung der Beziehung gesetzt werden müssen.

* Die Klassenspezifikation verlangt ein Hinweis darauf, ob es sich um eine Generalisierung oder Spezialisierung handelt. Wenn ersteres, dann sind auch die möglichen Spezialisierungen aufzuzählen!

* Wir unterscheiden drei Aggregationen:

(1) physische Gesamtheit-Teil-Struktur => keine 0-Kardinalitäten.

(2) Container-Inhalt-Struktur => meist beidseitige 0-Kardinalitäten.

(3) konzeptionelle Gesamtheit-Teil-Struktur oder Gruppierung-Mitglied-Struktur => oft nur beim Teilobjekt eine 0-Kardinalität.

* Die Objekt-ID gehört zum Design. Sie ist wichtig für OODB und verteilte Objekte in Netzen.

* Aggregierte Objekte sind Objekte, die andere Objekte enthalten (Layering oder gelayerte Objekte). Strukturtyp ist Whole-Part, also Gesamtheit-Teil-Struktur.

* Multiple Vererbung=mehrfache Vererbung, d.h. ein Objekt hat mehrere DIREKTE Basisklassen. Achtung: Konflikte wegen Namensgleichheit mgl. (Konflikt ist erst im OOD zu lösen!) => Auflösung der Hierarchie und Netzwerkbildung. Beispiel: Farbdrucker+Laserdrucker IS_A Farblaserdrucker.

* Cluster-Modell von Meyer: Cluster=Subjekte=logisch zusammengehörende Klassen. Cluster werden parallel in drei Phasen (SPEC, DESIMPL, VALGEN) modelliert, wobei zuerst generalisierte Cluster entwickelt werden sollten (für Reuse).

* Das Fontänenmodell operiert interativ mit Überschneidungen verschiedener Phasen und aber auch mit abgeschlossenen Phasen, so muß z.B. vor dem SW-Einsatz der SW-Test komplett abgeschlossen sein. Ist auch für nicht-objektorientierte SW geeignet.

* Baseball-Modell von Coad/Nicola: Von jeder Phase ein bißchen. Schnell Ergebnisse hervorbringen, aber Vorsicht: keine Vermengung von Designteilen (HIC, Poblembereich, Daten- und Task-Management) zu Beginn, die später wieder getrennt werden müssen. Notfalls auch zunächst Klassen mit nur einem Attribut entwerfen, die erst später angereichert werden.

* OA heißt: Modellierung des relevanten Realweltausschnitts. Betrachtet werden Klassen, Attribute, Methoden, Beziehungen, Strukturen, Subjekte, Kommunikationsverbindungen, Abstraktionen, Wertebereiche, Kardinalitäten, Defaultwerte, Spezifikationen, Algorithmen, ... in einem statischen (nach Coad bzw. Yourdon), einem dynamischen (nach Rumbaugh) und einem funktionellen Modell.

* Delegation heißt bei der OOE, daß ein Gesamtheitsobjekt aus Reuse-SW-ICs konstruiert wird, die jeweils bestimmte Aufgaben zu erledigen haben. Die Aggregation fungiert dabei hfg. als Antenne: Sie empfängt, womit die Teilobjekte weiterarbeiten können.

* Implizite Methoden, die nicht angezeigt werden müssen: Konstruktor, Destruktor, Zugriffsfunktionen, Beziehungsfunktionen (Auf- und Abbau).

* Ereignisfolgediagramme gehören zum dynamischen OOA-Modell. Alle Nachrichten für Externe, sowie Nachrichten von Externen werden modelliert, und es wird der Nachrichtenaustausch zwischen den Systemobjekten betrachtet. Die Diagramme bilden die diversen Szenarios ab (aber nicht 1:1 => ein Szenarioschritt=x Ereignisfolgen). Wenn Methoden auf sich selbst zeigen, ist dies im Diagramm entsprechend aufzunehmen. Konsistenzbedingung ist, daß nur alle Objekte (nicht nur Instanzen!) betrachtet werden, die auch im statischen OOA-Modell auftauchen (bis auf den externen User).

* Zustandsdiagramme: Aktivitäten sind eine temporäre, Timer-gesteuerte Angelegenheit, Aktionen dagegen immerzu möglich. Zustandsdiagramme für ein Objekt orientieren sich an der Gesamtheit der Ereignisfolgen, die dieses Objekt berühren. Aktivitäten und Aktionen sind bei den Zustandsübergängen zu vermerken. Bei der verbalen Spezifikation sind Wenn-dann-Bedingungen zu erläutern. Eine Konsistenzbedingung ist, daß man - außer vom Endzustand - immer zum Startzustand zurückkommen kann.

* Objektorientierte Datenflußdiagramme sind Strukturierte Analyse-Diagramme. Sie gehören zum funktionalen Modell. Struktogramme und Pseudocodes sind jedoch geeigneter zur Modellierung dieser Sicht.

* Upper-CASE-Tools beachten im Ggs. zu Low-CASE-Tools auch die Analysephase.

* Einige wichtige Namen+Werke: Boehm=Spiralenmodell, Chen=ERM, Coad=OOE, DeMarco=Strukturierte Analyse, Mellor=IM, Meyer=Cluster-Modell, Nicola=OOD/OOP, Rumbaugh=OOE, Shlaer=IM, Stroustrup=C++ und Yourdon=OOA.

* Zeiger auf Klassenelemente (Offset-Zeiger, die für alle Instanzen genutzt werden können; funktioniert auch mit static-Elementen [nicht bei Borland C++ 2.0]):

struct X {

int i; // wenn static, dann streikt Borland C++ 2.0

};

void main() {

int X::* I=&X::i; // I zeigt noch ins Leere

X x;

x.i=1;

X *xp=new X;

x->i=2;

cout << x.*I << xp->*I << endl;

};

* Unspezifizierte Argumente über den Elipse-Operator "...":

include <stdarg.h>

enum typ {ints, doubs}

void drucke(int anz, typ x, ...) {

va_list zva; // Zusatzargumente-Pointer

va_start(zva, x); // Tabelle über Typ informieren

switch(x) {

case ints:

for(int i; i<anz; i++)

cout << va_arg(zva, int) << "\t";

break;

case doubs:

for(int i; i<anz; i++)

cout << va_arg(zva, doubs) << "\t";

break;

};

va_end(zva);

};

void main() {

drucke(2, ints, 1, 2);

drucke(3, doubs, 3.3, 4.4, 5.5);

};

* Shift-Operatoren "<<" und ">>":

const int x=5; // in Borland C++ 2.0 KEIN modifizierbarer L-Wert

cout << (x << 5); // 5 * 2^5 = 5 * 32 = 160; x behält Wert=5

cout << (x >> 2); // 5 / 2^2 = 5 / 4 = 1

* Bit-Operatoren "&", "^" und "|": Es gilt die Tabelle:

Bit1 Bit2 Bit1 & Bit2 Bit1 ^ Bit2 Bit1 | Bit2

---------------------------------------------------------------------

0 0 0 0 0

0 1 0 1 1

1 0 0 1 1

1 1 1 0 1

* Abgeleitete Datentypen: Alle Datentypen, die aus den vordefinierten Datentypen wie z.B. "int", "double" und "char" gebildet werden, wobei die Operatoren "&", "*", "[]" und "()" verwendet werden. Beispiele:

int i[2][3]={{1,2,3}, {4,5,6}}; // abgeleiteter Typ i[2][3]

char *txtp=new char[10]; // abgeleiteter Typ *txtp

int &ri=i;

int f(int i){i++; return i;};

* Klassennamen sind nur dann extern gebunden, wenn folgende Bedingungen gegeben sind:

* Die Klasse enthält keine static-Variablen oder -Funktionen!

* Alle Funktionen müssen inline deklariert sein!

* Es darf in der Klasse kein extern gebundenes Objekt angesprochen werden!

* Literalkonstanten sind im Ggs. zu symbolischen Konstanten (const-Variablen) "selbstsprechende" Konstanten wie "123", "0217" oder "0x8f".

* Unvollständige Deklarationen sind Deklarationen der Art "class X;". Sie sind evtl. nötig, damit doppelt verkettete Klassen realisiert werden können. Beispiel:

class X;

class Y {

X *xp;

};

class X {

Y *yp;

};

* Zugriffsdeklarationen der Form "Klasse::Variable" sind in private abgeleiteten Klassen nötig, um Zugriff auf bestimmte Basisklassen-Elemente zu erhalten, da sich diese Elemente dann wie public-abgeleitete Elemente verhalten. Beispiel:

class X {

public:

int i;

};

class Y:private X {

public:

X::i; // Zugriffsdeklaration. Achtung: Kein Typ davor!

};

void main() {

Y y;

y.i=2; // ist möglich (ohne Zugriffdeklaration nicht)!

};

* Eingebettete Klassen haben keine besonderen Zugriffsrechte auf die äußere Klasse (das gilt auch umgekehrt). Grund: Auf die interne Klasse "Y" kann über "X::Y" zugegriffen werden, ohne daß ein "X" existieren muß! Ausnahme: Auf static-Elemente, Enumeratoren und typedef-Namen kann immer direkt zugegriffen werden (bei Borland C++ 2.0 kann eine Y-Klasse sogar im private-Teil von X stehen, und dennoch direkt mit "Y y;" Objekte davon erzeugt werden; dagegren streikt der Compiler beom Einsatz von static-Variablen)!

* Speicheraufbau pro Programm:

- Codeteil: Enthält die Anweisungen.

- Datenteil: Enthält die gloabeln Variablen und die static-Variablen.

- Stack: Enthält die lokalen Variablen.

- Heap: Enthält die benutzergesteuerten Variablen, für die ein Zeiger im Datenteil oder Stack nötig ist, da sie namenlos sind.

* L-Wert (L-Value): Das sind i.d.R. modifizierbare Variable-Inhalte. Bei einigen Operatorten müssen zwingend L-Werte verwendet werden. Unveränderliche L-Werte sind z.B. Namen für Felder, Funktionen Literal-Konstanten und const-Variablen.

* Leeranweisungen der Form ";" sind bisweilen aus syntaktischen Gründen nötig in C++.

* Unbezeichnete Klassen: Das sind Klassen-Definitionen der Form:

class {

int i;

} x, y, z; // x, y, z nur als unspezifizierte Argumente übergebbar

* Namen ohne Bindung sind Namen, für die der Compiler keinen Speicherplatz anfordern muß. Dazu gehören Enumeratoren und typedef-Namen.

* Zwei Dinge sind bei Template-Funktionen zu beachten:

(1) Die Template-Argumente <x, y, z> müssen alle als Argumente der Funktion Verwendung finden. Die Funktion dagegen kann noch weitere Argumente führen.

(2) die Schlüsselwörter "static", "inline" und "extern" dürfen nur hinter "template <...>" stehen, nicht davor.

* Typendungen: "l", "f" und "u" für Literal-Konstanten, wobei die Reihenfolge von "f"/"l" und "u" unwichtig ist.

* Typumwandlungsformen:

- Cast-Noation: int i=(int)j;

- Funktions-Notation: int i=int(j);

* Es gilt: Realität > Problembereich > System-Verantwortlichkeit = Objekte, Strukturen, Beziehungen, Nachrichten-Verbindungen.

* Aggregations-Strukturen sind folgendermaßen zu überprüfen:

- In welche Teile kann die Aggregationsklasse zerlegt werden?

- Ist die Systemverantwortlichkeit für alle Teile gegeben?

- Enthalten die Teilklassen die nötigen Anforderungen?

* Ereignisse sind zeitpunkt-abhängig, Zustände umfassen dagegen Zeiträume. Es gilt die Kette: Ereignis -> Zustand -> Ereignis -> Zustand ...

* Aktionen bzw. Aktivitäten werden oft durch Ereignisse wie z.B. eintreffende Nachrichten ausgelöst. Aktionen sind zeitunabhängige Operationen von Objekten, wie z.B. "Lesen", "Schreiben", "Nachrichtenversand". Aktivitäten, z.B. komplexe Methodenaufrufe, benötigen dagegen eine bestimmte Zeit zur Ausführung, was i.d.R. durch Timer realisiert wird.

* Attribute werden folgendermaßen geprüft: Keine Redunanzen enhalten? Keine ableitbaren Informationen enthalten? Existiert immer ein sinnvoller Wert (wenn nicht, Spezialklasse bilden)? Einziges Attribut einer Klasse?

* Die OOA besteht aus folgenden Komponenten:

(1) statisches Modell: Attribute-, Methoden-, Objekte-, Subjekte-, Nachrichten-Verbindungs-Schicht zur Modellierung der Problem-Bereichs-Komponete im Bereich der System-Verantwortlichkeit mit interner Konsistenzprüfung und den Klassen-Spezifikationen.

(2) dynamisches Modell: Szenarien pro Objekt, Ereignis-Folge-Diagramme pro Szenario und Zustands-Diagramme pro Objekt.

(3) funktionales Modell: Pseudocodes und Struktogramme für alle komplexeren Methoden. Evtl. auch noch Strukturierte Analysen und Fluß-Diagramme.

* Die Klassen-Spezifikationen des statischen Modells sind folgendermaßen aufgebaut:

KLASSE <name, spezialisierung, generalisierung, beschreibung)

TEILKLASSE <name, kardinalität, strukturtyp, beschreibung>

TEILKLASSE ...

BEZIEHUNG <objektname, kardinalität, beschreibung>

BEZIEHUNG ...

ATTRIBUT <name, typ, wertebereich, beschreibung>

ATTRIBUT ...

METHODE <name, argumente, rückgabewert, beschreibung>

METHODE ...

NACHRICHTENVERBINDUNG <obj.name, methode, argumente, rückgabewert, beschreibung>

NACHRICHTENVERBINDUNG ...

* Gefundene Klassen sind vielfältig zu untersuchen: Klassenwissen nötig? Klassenwissen ausreichend? Mehr als ein Attribut? Mehr als eine Instanz? Gelten Attribute/Methoden für alle Objekte? Keine ableitbaren Attribute enthalten? Wird Problembereich abgebildet (ohne OOA- und OOP-Konstrukte)?

* Konsistenzprüfungen sind nicht nur in den einzelnen OOA-Modellen (statisch/dynamisch/funktional) vorzunehmen, sondern auch zwischen den Modellen. Achtung: Implizite Methoden sind hiervon auszuklammern! Beim ersten Durchlauf des Baseball-Modells muß jedoch z.B. keine statischen Modell-Änderung vorgenommen werden, wenn im dynamischen Modell abweichende Methoden im Ereignis-Folge-Diagrammen auftauchen.

* Die Szenarien geben alle User-Aktionen und System-reaktionen wieder. Nur wenn die Reaktion dem User angezeigt wird, ist für sie ein Extra-Schritt anzugeben; interne Prüfungen usw. werden im gleichen Schritt mit der User-Aktion wiedergegeben.

* Neben Klassen-Spezifikation des statischen Modells der OOA gibt es auch Zustands-Spezifikationen der dynamischen Modells der OOA. Sie sind folgendermaßen aufgebaut:

ZUSTAND <name, beschreibung, attributwert>

EREIGNIS <name, sender, argumente, aktion, folgezustand>

 

 


| Home | News | Software | Bilder | Texte | Börse | Favoriten | Goddesses | Winsock | Diplomarbeit | Titel | Inhalt | Einleitung | Kapitel 2 | Kapitel 2-1 | Kapitel 2-2 | Kapitel 2-3 | Kapitel 3 | Kapitel 3-1 | Kapitel 3-2 | Kapitel 3-3 | Ausblick | Literatur | Anhang | Alles fliesst | Comics | Musik | Leben | Links | Sitemap | Admin |

© by DanPHPEd - Letzte Änderung: 07. März 2009