Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
1 Einleitung
2 Die Basis der Objektorientierung
3 Die Prinzipien des objektorientierten Entwurfs
4 Die Struktur objektorientierter Software
5 Vererbung und Polymorphie
6 Persistenz
7 Abläufe in einem objektorientierten System
8 Module und Architektur
9 Aspekte und Objektorientierung
10 Objektorientierung am Beispiel: Eine Web-Applikation mit PHP 5 und Ajax
A Verwendete Programmiersprachen
B Literaturverzeichnis
Stichwort
Ihre Meinung?

Spacer
 <<   zurück
Objektorientierte Programmierung von Bernhard Lahres, Gregor Rayman
Das umfassende Handbuch
Buch: Objektorientierte Programmierung

Objektorientierte Programmierung
2., aktualisierte und erweiterte Auflage, geb.
656 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1401-8
Pfeil 4 Die Struktur objektorientierter Software
  Pfeil 4.1 Die Basis von allem: das Objekt
    Pfeil 4.1.1 Eigenschaften von Objekten: Objekte als Datenkapseln
    Pfeil 4.1.2 Operationen und Methoden von Objekten
    Pfeil 4.1.3 Kontrakte: Ein Objekt trägt Verantwortung
    Pfeil 4.1.4 Die Identität von Objekten
    Pfeil 4.1.5 Objekte haben Beziehungen
  Pfeil 4.2 Klassen: Objekte haben Gemeinsamkeiten
    Pfeil 4.2.1 Klassen sind Modellierungsmittel
    Pfeil 4.2.2 Kontrakte: die Spezifikation einer Klasse
    Pfeil 4.2.3 Klassen sind Datentypen
    Pfeil 4.2.4 Klassen sind Module
    Pfeil 4.2.5 Sichtbarkeit von Daten und Methoden
    Pfeil 4.2.6 Klassenbezogene Methoden und Attribute
    Pfeil 4.2.7 Singleton-Methoden: Methoden für einzelne Objekte
  Pfeil 4.3 Beziehungen zwischen Objekten
    Pfeil 4.3.1 Rollen und Richtung einer Assoziation
    Pfeil 4.3.2 Navigierbarkeit
    Pfeil 4.3.3 Multiplizität
    Pfeil 4.3.4 Qualifikatoren
    Pfeil 4.3.5 Beziehungsklassen, Attribute einer Beziehung
    Pfeil 4.3.6 Implementierung von Beziehungen
    Pfeil 4.3.7 Komposition und Aggregation
    Pfeil 4.3.8 Attribute
    Pfeil 4.3.9 Beziehungen zwischen Objekten in der Übersicht
  Pfeil 4.4 Klassen von Werten und Klassen von Objekten
    Pfeil 4.4.1 Werte in den objektorientierten Programmiersprachen
    Pfeil 4.4.2 Entwurfsmuster »Fliegengewicht«
    Pfeil 4.4.3 Aufzählungen (Enumerations)
    Pfeil 4.4.4 Identität von Objekten

In diesem Kapitel beschreiben wir die grundlegende Struktur von objektorientierten Softwaresystemen. Wir beginnen dabei mit dem Grundlegenden: mit dem Objekt an sich. Danach gehen wir auf die Klassifizierung von Objekten ein und werden uns dann detailliert mit den Klassen und den Beziehungen zwischen Objekten beschäftigen.

4 Die Struktur objektorientierter Software


Rheinwerk Computing - Zum Seitenanfang

4.1 Die Basis von allem: das Objekt  Zur nächsten ÜberschriftZur vorigen Überschrift

In diesem Abschnitt werden Sie zunächst das zentrale Konstrukt der objektorientierten Programmierung kennen lernen, nämlich das namensgebende Objekt. Ausgehend von einer formalen Definition werden wir die wichtigsten Eigenschaften von Objekten vorstellen.


Icon Hinweis Objekte in der objektorientierten Programmierung

Ein Objekt ist ein Bestandteil eines Programms, der Zustände enthalten kann. Diese Zustände werden von dem Objekt vor einem Zugriff von außen versteckt und damit geschützt.

Außerdem stellt ein Objekt anderen Objekten Operationen zur Verfügung. Von außen kann dabei auf das Objekt ausschließlich zugegriffen werden, indem eine Operation auf dem Objekt aufgerufen wird.

Ein Objekt legt dabei selbst fest, wie es auf den Aufruf einer Operation reagiert. Die Reaktion kann in Änderungen des eigenen Zustands oder dem Aufruf von Operationen auf weiteren Objekte bestehen.


Objekt = Daten + Funktionalität

Technisch betrachtet ist ein Objekt im Sinne des objektorientierten Programmierens also eine Zusammenfassung von Daten (Zuständen des Objekts) und der dazugehörigen Funktionalität (den vom Objekt unterstützten Operationen).

Zentral ist hier zunächst die Aussage, dass ein Objekt seine Zustände vor einem direkten Zugriff von außen schützt. Das Objekt kapselt also die Daten, die seinen Zustand ausmachen. Häufig wird der Aufruf einer Operation auf einem Objekt auch als Senden einer Nachricht an das Objekt bezeichnet.

Im folgenden Abschnitt 4.1.1 stellen wir zunächst die Fähigkeiten von Objekten zur Datenkapselung vor, bevor wir dann in Abschnitt 4.1.2 auf die von Objekten zur Verfügung gestellten Operationen genauer eingehen. Anschließend stellen wir in Abschnitt 4.1.3 vor, wie Objekte für die von ihnen zur Verfügung gestellten Operationen verantwortlich sind und Kontrakte dafür festlegen können. Dass ein Objekt immer eine klar definierte Identität hat, die es von allen anderen Objekten unterscheidet, ist Gegenstand von Abschnitt 4.1.4. Die Vorstellung von Objekten wird mit Abschnitt 4.1.5 abgeschlossen mit einem Überblick über die Arten von Beziehungen, die Objekte miteinander haben.

Diskussion: Ist Java überhaupt objektorientiert?

Gregor: Wenn wir die oben genannte Definition von Objekten nehmen, dann sind doch viele Objekte, die zum Beispiel in Java-Programmen verwendet werden, gar keine echten Objekte.

Bernhard: Wieso meinst du das? Objekte in Java-Programmen haben doch eigene Daten und stellen nach außen hin Operationen zur Verfügung.

Gregor: Ja, schon. Aber die Definition fordert ja, dass die Zustände eines Objekts grundsätzlich nicht direkt nach außen sichtbar gemacht werden. In Java kann es aber durchaus sein, dass die Daten eines Objekts nach außen sichtbar sind. Die zugehörige Klasse muss die Datenelemente lediglich als public deklarieren. Außerdem kann ich in Java bei Aufruf einer Operation auch noch andere Dinge tun, als den internen Zustand des Objekts ändern oder Operationen auf anderen Objekten aufrufen.

Bernhard: Du hast Recht. Viele Programmiersprachen, die als objektorientiert gelten, erzwingen es nicht, dass man darin auch rein objektorientierte Programme erstellt. So ist es zum Beispiel in Java möglich, die Kapselung der Daten aufzuheben, indem die Daten eines Objekts über die Sichtbarkeitsregeln der zugeordneten Klasse komplett öffentlich gemacht werden. Diese Daten gehören damit nicht mehr zum geschützten internen Objektzustand. Smalltalk zum Beispiel erzwingt viel stärker als Java die Verwendung von rein objektorientierten Verfahren. Allerdings hat sich auch in Java die Konvention herausgebildet, dass Datenelemente in der Regel nicht direkt öffentlich gemacht werden. Stattdessen werden häufig sogenannte Getter- und Setter-Methoden definiert, mit denen diese Datenelemente ausgelesen oder neu gesetzt werden können.
Rheinwerk Computing - Zum Seitenanfang

4.1.1 Eigenschaften von Objekten: Objekte als Datenkapseln  Zur nächsten ÜberschriftZur vorigen Überschrift

Ein Objekt hat Eigenschaften, die ihm direkt zugeordnet werden können. Ein Haus hat seine Breite, Tiefe, Höhe, Wohnfläche, es hat eine Farbe, eine Lage, ein Alter. Wenn wir das Haus in einer objektorientiert entwickelten Anwendung abbilden wollen, erstellen wir ein Objekt Haus, das diese Werte als seine Eigenschaften verwalten wird. In Abbildung 4.1 ist eine Sichtweise auf ein Haus dargestellt, die einige der Eigenschaften beschreibt.

Abbildung 4.1    Grundriss eines Hauses

Dass ein Objekt bestimmte Eigenschaften hat, ist allerdings nicht gleichbedeutend damit, dass das Objekt auch für alle diese Eigenschaften jeweils direkt zugeordnete Daten besitzt. Deshalb unterscheiden wir bei Objekten zwischen den Daten des Objekts und den zugeordneten Eigenschaften.


Icon Hinweis Daten eines Objekts

Ein Objekt kann Werte zugeordnet haben, die nur von ihm selbst verändert werden können. Diese Werte sind die Daten, die das Objekt besitzt. Von außen sind die Daten des Objekts nicht sichtbar und nicht zugreifbar.


Daten sind also die Repräsentanten des internen Zustands eines Objekts. Ein Objekt Haus könnte also durchaus als Wert das Alter des Hauses als Dateneintrag besitzen. Alternativ (und in den meisten Fällen sinnvoller) kann das Baujahr gespeichert werden. Die Art der Datenhaltung innerhalb des Objekts ist nach außen aber nicht sichtbar.

Objekte besitzen Daten.

Ein Objekt besitzt also Daten. Im Gegensatz zu Datenstrukturen im strukturierten Programmieren, wo Datenstrukturen die Daten lediglich enthalten, kann man bei Objekten wirklich vom Besitzen sprechen. Beim strukturierten Programmieren kann jedes Unterprogramm auf die Daten einer Datenstruktur zugreifen. Beim objektorientierten Programmieren entscheidet das Objekt selbst, wer und wie auf die Daten zugreifen und sie ändern kann.


Icon Hinweis Eigenschaften (Attribute) von Objekten

Objekte haben Eigenschaften, die von außen erfragt werden können. Dabei kann von außen nicht unterschieden werden, ob eine Eigenschaft direkt auf Daten des Objekts basiert oder ob die Eigenschaft auf der Grundlage von Daten berechnet wird. Eigenschaften, die nicht direkt auf Daten basieren, werden abgeleitete Eigenschaften genannt.


Die Eigenschaften eines Objekts müssen nicht notwendigerweise voneinander unabhängig sein. Die Fläche eines Hauses wird mit seiner Breite und Tiefe zusammenhängen. Wie die Eigenschaften zusammenhängen, wie sie sich ändern lassen und wie sie sich bei einer Änderung beeinflussen, das gehört zu der logischen Funktionalität des Objekts. Für die Verwaltung der Daten, auf welche die Eigenschaften des Objekts abgebildet werden, ist die Implementierung des Objekts zuständig.

Kapselung von Daten

Die Kapselung der Daten wird durch ein Objekt selbst sichergestellt. Nur das Objekt selbst sollte mit seinen Daten arbeiten und die Datenstrukturen kennen. Die Kapselung der Daten ist besonders dann wichtig, wenn die abgebildeten Eigenschaften des Objekts voneinander abhängig sind. In solchen Fällen werden oft nur bestimmte Eigenschaften direkt auf Daten abgebildet, die Werte der anderen werden aus den gespeicherten Daten berechnet.

Machen wir es nun etwas spannender und spielen ein wenig mit der elektrischen Spannung und dem Strom. Unser Beispielobjekt wird eine Stromleitung repräsentieren, für die das Ohm´sche Gesetz gilt:

    • U = R·I

Für die übertragene Leistung der Leitung gilt:

    • W = U·I

Dabei ist U die elektrische Spannung, R der Widerstand der Leitung, I der übertragene Strom, und W ist die übertragene Leistung.

Abbildung 4.2    Schematische Darstellung eines Stromkreises

Wir betrachten hier vier Eigenschaften der elektrischen Leitung, brauchen aber nur zwei davon als Daten zu speichern. Die nicht gespeicherten Werte können durch die oben aufgeführten Gleichungen berechnet werden. Für die Anwender des Objekts ist es nicht relevant, welche zwei Werte es sind. Sie können zum Beispiel den Wert für Stromstärke und Widerstand speichern und die Werte für Spannung und Leistung bei Bedarf daraus berechnen.

Die Implementierung der Funktionalität des Objekts ist gekapselt und spielt für die Anwender des Objekts keine Rolle. Es ist nun abhängig von der Betrachtungsebene, die Sie einnehmen wollen, wie Sie die Eigenschaften des Objekts ElektrischeLeitung in einem Modell abbilden.


Darstellung eines Objekts in UML

In der Sprache UML bildet man ein Objekt einfach als ein Rechteck ab. In Abbildung 4.3 ist ein Beispiel dafür zu sehen. Oben in dem Rechteck steht der unterstrichene Name des Objekts, und darunter in getrennten Abteilungen befinden sich die Daten und die Operationen bzw. die Methoden des Objekts. Laut der UML-Spezifikation bezeichnet man abgeleitete Eigenschaften eines Objekts mit einem Schrägstrich.


Die UML-Diagramme können verwendet werden,

  • um die nach außen sichtbare Schnittstelle darzustellen und
  • um sie für die Darstellung der gekapselten Struktur der Implementierung zu nutzen.

Darstellung der äußeren Struktur

Für die Darstellung der äußeren Struktur der Objekte ist es nicht relevant, welche der Eigenschaften direkt als Daten gespeichert werden und welche von ihnen abgeleitet sind und berechnet sind. In so einer Darstellung bezeichnet man mit einem Schrägstrich die voneinander abhängigen Eigenschaften des Objekts. Alternativ oder zusätzlich kann man die Art der Abhängigkeit, zum Beispiel eine Formel, in geschweiften Klammern angeben.

In Abbildung 4.3 sehen Sie eine Darstellung des Objekts elektrischeLeitung, in der alle Eigenschaften aufgeführt sind. Dort ist allerdings nicht festgelegt, welche davon durch Daten repräsentiert und welche aus den Daten abgeleitet werden.

In dem mit markierten Kästchen wird eine Einschränkung in geschweiften Klammern angegeben. Hier werden die Abhängigkeiten der Attribute untereinander angezeigt. In dem mit versehenen Abteil des Kästchens finden Sie die Attribute oder die Datenelemente des Objekts. Sie sind in unserem Beispiel nicht unabhängig und daher mit vorangestellten Schrägstrichen versehen. Wie Sie an der Markierung erkennen, wird ein Objekt in UML in einem rechteckigen Kästchen dargestellt. Der Name des Objekts wird unterstrichen und der Typ des Objekts steht dabei hinter einem Doppelpunkt.

Abbildung 4.3    Darstellung der äußeren Struktur eines Objekts

Darstellung der Implementierung

Bei der Implementierung müssen Sie sich dann allerdings entscheiden, welche Eigenschaften des Objekts direkt auf Daten abbilden und welche sich aus diesen Daten ableiten. In Abbildung 4.4 ist diese Entscheidung getroffen.

Abbildung 4.4    Implementierungssicht auf ein Objekt

An dem oberen Hinweispfeil der Markierung sehen Sie, dass die Eigenschaften widerstand und spannung direkt als Daten repräsentiert werden. Für die anderen Eigenschaften ist angegeben, wie sie sich aus den Daten ableiten. Die Eigenschaften strom und leistung sind also abgeleitete Eigenschaften.

Warum ist Datenkapselung wichtig?

Bisher haben wir in diesem Abschnitt vorgestellt, wie ein Objekt seine Eigenschaften verwaltet und die ihm zugeordneten Daten nach außen vor einem direkten Zugriff schützt. Dabei wurde auch deutlich, dass von außen nicht ersichtlich sein soll, welche Eigenschaften überhaupt als Daten repräsentiert werden.

Aber warum ist dies eigentlich eine wünschenswerte Vorgehensweise? In vielen Fällen könnte es doch viel einfacher sein, wenn die betroffenen Daten direkt geändert werden könnten.

Strukturierte Programmierung

Betrachten Sie deshalb als Ausgangspunkt die Situation und die damit verbundenen Probleme, wie sie in der strukturierten Programmierung vorliegen. Die Trennung der Daten vom Code bringt dort nämlich ein Problem mit sich: Da es nur eingeschränkt möglich ist, die Datenstrukturen konkreten Routinen zuzuordnen, ist es nötig, dass die Strukturen der Daten für jeden Teil des Programms sichtbar und zugänglich sind.

Icon Beispiel Zugriff auf Daten eines Kreises

Nehmen Sie als Beispiel eine Datenstruktur, die einen Kreis auf dem Bildschirm beschreibt. Diese Struktur würde die x- und y-Koordinaten des Mittelpunkts, den Radius, die Farbe und andere Attribute des Kreises beinhalten. Abbildung 4.5 zeigt einen Kreis und verschiedene Aufrufer, die Änderungen an der Kreisstruktur vornehmen.

Abbildung 4.5    Unregulierter Zugriff auf Daten eines Kreises

Wie in der Abbildung ersichtlich, arbeiten nun mehrere Routinen mit den Daten des Kreises. Die Prozedur paintCircle benötigt die Daten, um den Kreis auf dem Bildschirm darzustellen, die Prozedur moveCircle ändert die Daten in der Datenstruktur und veranlasst, dass die Darstellung aktualisiert wird. Andere Prozeduren können die Daten modifizieren, um zum Beispiel den Radius des Kreises zu ändern oder um Schnittpunkte mit einer Geraden zu berechnen oder um festzustellen, ob die Position des Mauszeigers sich gerade innerhalb des Kreises befindet.

Hier liegt schon ein erstes Problem: Die Prozedur moveCircle ist dafür vorgesehen, den Kreis zu verschieben und die Darstellung zu aktualisieren. Es lässt sich aber nicht verhindern, dass ein Aufrufer direkt die Koordinaten des Kreises ändert. In diesem Fall bleibt die Aktualisierung der Anzeige aus.

Wenn Sie die Struktur des Kreises ändern und zum Beispiel eine neue Eigenschaft Füllmuster hinzufügen: Wie finden Sie heraus, welche Stellen im Programm angepasst werden müssen, damit diese Eigenschaft korrekt behandelt wird?

Datenkapselung als Lösung

Hier bietet die Datenkapselung in Objekten eine elegante Lösung. Wenn Sie statt einer reinen Datenstruktur ein Objekt Kreis verwenden, wird der Zugriff auf die nun internen Daten des Objekts nur noch über die vom Objekt zugelassenen Verfahren erfolgen können. In Abbildung 4.6 ist dargestellt, dass bei einem Objekt alle Versuche, direkt auf die internen Daten zuzugreifen, abgewiesen werden.

Abbildung 4.6    Kein direkter Zugriff auf Daten des Objekts »Kreis«

Eine komplette Blockade des Zugriffs auf die Daten alleine ist natürlich noch nicht ausreichend. Stattdessen muss ein Objekt auch Operationen anbieten, mit denen es modifiziert werden kann. In unserem Beispiel sind das die Operationen move() und paint(), die direkt auf dem Objekt aufgerufen werden. Dabei ruft die Operation move() selbst wieder die Operation paint() auf und stellt so sicher, dass der Kreis nach einem Verschieben auch korrekt neu angezeigt wird.

Im folgenden Abschnitt werden wir Operationen als Bestandteil der Schnittstelle eines Objekts vorstellen.

Diskussion: Nachteile der Datenkapselung

Gregor: Das mit der Datenkapselung ist ja eine nette Idee. Aber wenn immer nur das Objekt selbst mit seinen Daten arbeiten kann, hat das in bestimmten Situationen auch Nachteile.

Bernhard: Ja, du hast tatsächlich Recht. Diese Regel kann dazu führen, dass das Prinzip einer einzigen Verantwortung verletzt wird. Wir bürden damit nämlich dem Objekt alle Verantwortungsbereiche auf, die nur unter Verwendung seiner Daten bearbeitet werden können. Es gibt durchaus Fälle, in denen die Kapselung der Daten in Frage gestellt wird.

Gregor: An welche Fälle denkst du dabei?

Bernhard: Nehmen wir als Beispiel einmal an, dass unsere Anwendung einen Architekten beim Entwerfen von Häusern unterstützen soll. [Die Softwareentwicklung oder zumindest der Entwurf von Software wird häufig mit dem Bauwesen und der Bauarchitektur verglichen. Wir glauben nicht, dass diese Analogie stimmt und dass sie eher verwirrt, als für besseres Verständnis sorgt. Zum Glück wird diese Analogie nur von den Softwareentwicklern und nicht von den Baumeistern verwendet. Es wäre tragisch, wenn die Erzeugnisse der Bauarchitekten so häufig abstürzen würden wie die Endprodukte mancher Softwarearchitekten. Dennoch konnten wir es uns nicht verkneifen, einen Bauarchitekten zumindest in einem Beispiel zu erwähnen. ] Diese objektorientierte Anwendung wird Objekte enthalten, die Häuser repräsentieren. Auf dieser Grundlage können wir statische Berechnungen durchführen oder den Energiebedarf des Hauses ermitteln. Gleichzeitig aber sollten diese Daten in eine Datei gespeichert und wieder ausgelesen werden. Wenn wir jetzt darauf bestehen, dass nur ein Objekt, das Objekt Haus, auf die Daten zugreifen kann, würde dieses Objekt zumindest zwei Verantwortungen tragen müssen: die für die fachliche Funktionalität und die für die Datenspeicherung.

Gregor: Wäre es denn so schlimm, wenn in diesem Fall ein Objekt eben einmal die Verantwortung für zwei Aufgaben übernimmt?

Bernhard: Das ist ja noch nicht alles. Bei einer strikten Datenkapselung würden wir die Aufgabe der Speicherung zumindest teilweise auch über andere speicherbare Objekte verteilen. Eine Verantwortung wäre also keineswegs einem Modul zugeordnet, sondern diffus über die ganze Anwendung verschmiert und mit anderen Verantwortungen vermischt. Kaum das Ergebnis, das wir erreichen wollten.

Gregor: Aber was dabei auffällt: Die zwei angesprochenen Aspekte der Funktionalität liegen in verschiedenen Domänen der Anwendung: eine in der fachlichen Domäne »Bauwesen«, die andere in der technischen Domäne »Datenspeicherung«.

Bernhard: Da hast du Recht. Wir sollten deshalb die Forderung nach der Kapselung der Daten leicht umformulieren: Auf die Daten eines Objekts in einer Domäne sollte innerhalb dieser Domäne nur das Objekt selbst zugreifen. Die Bauwesenfunktionalität des Objekts Haus sollte also nicht direkt auf die Daten der Objekte Zimmer zugreifen.

Die Kapselung der Daten führt also nicht automatisch dazu, dass das Prinzip einer einzigen Verantwortung eingehalten werden kann. Die klare Strukturierung der verschiedenen Aspekte einer Anwendung ist eins der Probleme, für deren Lösung die Objektorientierung alleine manchmal nicht ausreicht. Eine mögliche Lösung für dieses Problem werden wir in Kapitel 9, »Aspekte und Objektorientierung«, vorstellen.


Rheinwerk Computing - Zum Seitenanfang

4.1.2 Operationen und Methoden von Objekten  Zur nächsten ÜberschriftZur vorigen Überschrift

Wir haben nun also gesehen, wie ein Objekt die ihm zugeordneten Daten vor unerwünschten Zugriffen schützt und warum das wünschenswert ist. Der zweite Bestandteil, der in der Definition des Begriffs Objekt auftaucht, ist die Fähigkeit von Objekten, auf den Aufruf von Operationen zu reagieren.

Ein Objekt stellt dem Rest der Anwendung Operationen bereit. Operationen stellen das Verhalten und die Funktionalität des Objekts dar, es sind die ausführbaren Routinen, die das Objekt anderen Teilen der Anwendung anbietet.


Icon Hinweis Operationen von Objekten

Operationen spezifizieren, welche Funktionalität ein Objekt bereitstellt. Unterstützt ein Objekt eine bestimmte Operation, sichert es einem Aufrufer damit zu, dass es bei einem Aufruf die Operation ausführen wird. Durch die Operation wird dabei zum einen die Syntax des Aufrufs vorgegeben, also zum Beispiel für welche Parameter Werte eines bestimmten Typs zusammen mit dem Aufruf übergeben werden müssen.

Zum anderen werden dadurch auch Zusicherungen darüber gemacht, welche Resultate die Operation haben wird.


Wenn wir nun alle Operationen zusammenfassen, die ein Objekt unterstützt, so haben wir eine komplette Beschreibung der Funktionalität des Objekts vorliegen. Wir kennen damit also die Schnittstelle des Objekts zur Außenwelt.


Icon Hinweis Schnittstelle eines Objekts

Die Schnittstelle eines Objekts ist die Menge der Operationen, die das Objekt unterstützt. Die Schnittstelle sagt nichts über die konkrete Realisierung der Operationen aus.


Wenn ein Objekt eine Operation unterstützt, so muss dieses Objekt zur Umsetzung der Operation eine Methode besitzen. Eine Operation entspricht immer einer Methode des Objekts. Welche Methode einer Operation eines konkreten Objekts entspricht, ist eine interne Angelegenheit des Objekts. Die gleiche Operation kann bei verschiedenen Objekten unterschiedlichen Methoden entsprechen.


Icon Hinweis Methoden

Methoden von Objekten sind die konkreten Umsetzungen von Operationen. Während Operationen die Funktionalität nur abstrakt definieren, sind Methoden für die Realisierung dieser Funktionalität zuständig.


Die konkrete Implementierung der Methoden eines Objekts ist nur für das Objekt selbst relevant. Andere Objekte, welche die Funktionalität des Objekts verwenden – die also Operationen des Objekts nutzen –, müssen und sollen die Implementierung der Methoden nicht kennen. Getreu dem Motto: »Was ich nicht weiß, macht mich nicht heiß« – oder in diesem Fall »... macht mich nicht abhängig«.

Trennung der Schnittstelle von der Implementierung

Ein Objekt kapselt also die Implementierung der Operationen und trennt so die Schnittstelle des Objekts von der Implementierung. Die Methoden eines Objekts können mit den Daten des Objekts und mit den Parametern der Methoden arbeiten.

Ein Objekt kann Methoden haben, die nur es selbst verwenden kann, nicht jede Methode muss also einer Operation entsprechen. Wir werden auf dieses Thema in Abschnitt 4.2.5, »Sichtbarkeit von Daten und Methoden«, näher eingehen.

Die meisten Objekte besitzen ihre eigenen Daten. Auch wenn zwei Objekte zu demselben Typ gehören, hat jedes der Objekte seine eigenen Daten. Eine elektrische Leitung kann zum Beispiel die Spannung 230 V haben, eine andere nur 12 V. Bei den Methoden ist es ein wenig anders, denn das Verhalten der Objekte, die zum demselben Typ (derselben Klasse) gehören, unterliegt meistens denselben Regeln. Deswegen werden die Methoden, welche die Operationen der Objekte umsetzen, in der Regel den Klassen und nicht direkt einzelnen Objekten zugeordnet (zum Thema Klassen und Klassifizierung der Objekte siehe Abschnitt 4.2).

Da man Operationen und Methoden für ganze Klassen der Objekte und nicht für die einzelnen Objekte selbst deklariert, bietet der UML-Standard keine Möglichkeit, die Operationen und Methoden für einzelne Objekte darzustellen. Man stellt sie immer für ganze Klassen der Objekte dar.

Abbildung 4.7    Mögliche Operationen und Methoden einer elektrischen Leitung

In Abbildung 4.7 sehen Sie die Operationen der Klasse ElektrischeLeitung. Diese können von jedem Objekt, das zu dieser Klasse gehört, also von jeder elektrischen Leitung, genutzt werden. Dabei werden die Operationen und Methoden einer ganzen Klasse von Objekten unter den Attributen (siehe Markierung ) angegeben. Wie Sie in der Abbildung an den Markierungen und erkennen, ist es bei den dargestellten einzelnen Objekten nicht mehr notwendig, alle Operationen und Methoden anzugeben. Welche Operationen ein Objekt unterstützt, und welche Methoden es für ihre Umsetzung verwendet, ergibt sich aus der Klassenzugehörigkeit des Objekts.

Wir werden im Folgenden das aufgeführte Beispiel in der Sprache JavaScript umsetzen.

Warum Beispiele in JavaScript?

JavaScript ist eine einfache objektorientierte Skriptsprache [In Anhang A.1 finden Sie eine kurze Übersicht über die objektorientierten Eigenschaften von JavaScript. ] . Sie wird meistens verwendet, um Internetseiten dynamisch zu gestalten, kann aber auch als Skriptsprache unter Windows verwendet werden. Allerdings unterstützt JavaScript nicht alle Konzepte der Objektorientierung. Datenkapselung wird in JavaScript nicht ausreichend unterstützt, da auf die Daten von Objekten direkt zugegriffen werden kann.

Aber JavaScript ist auch eine objektorientierte Programmiersprache, die ohne Klassen auskommt. Wir verwenden sie deshalb an dieser Stelle als Beispiel, um zu zeigen, dass ein Arbeiten mit Objekten auch möglich ist, ohne diese Objekte bereits konkreten Klassen zuzuordnen.

In Listing 4.1 sind die Methoden aus Abbildung 4.7 für ein Objekt elektrisch umgesetzt.

if (typeof(window) != "undefined") {  
    echo = window.alert; 
} else if (typeof(WScript) != "undefined") { 
    echo = function(text) { 
        Wscript.Echo(text); 
    } 
} 
elektrisch = new Object;  
elektrisch.spannung = 230.0;  
elektrisch.widerstand = 1000.0; 
elektrisch.spannungLesen = function() {  
    return this.spannung; 
} 
elektrisch.spannungSetzen = function(spannung) {  
    this.spannung = spannung; 
} 
elektrisch.widerstandLesen = function() {  
    return this.widerstand; 
} 
elektrisch.widerstandSetzen = function(widerstand) {  
    if (widerstand > 0) this.widerstand = widerstand; 
} 
elektrisch.stromLesen = function() { 
    return this.spannungLesen() / this.widerstandLesen();  
} 
elektrisch.stromSetzen = function(strom) { 
    if (strom > 0) { 
        this.widerstand = this.spannung / strom;  
    } 
} 
elektrisch.leistungBerechnen = function() { 
    return this.spannungLesen() * this.stromLesen();  
} 
elektrisch.aus = function() {  
    this.spannung = 0; 
    echo("Die elektrische Leitung wurde abgeklemmt"); 
} 
elektrisch.stromSetzen(1); 
echo("Strom: " + elektrisch.stromLesen() + "A"); 
echo("Leistung: " + elektrisch.leistungBerechnen() + "W"); 
echo("Widerstand: " + elektrisch.widerstandLesen() + "Ohm"); 
elektrisch.aus();

Listing 4.1    Umsetzung und Verwendung einer elektrischen Leitung

JavaScript kommt ohne Klassen aus.

Das Beispiel in Listing 4.1 zeigt ein Objekt, das eine Reihe von Operationen anbietet.

In der mit markierten Zeile haben wir unser Objekt elektrisch erstellt, in den mit markierten Zeilen wurden dem Objekt Werte für zwei Datenelemente zugeordnet. Das Objekt speichert in dieser Implementierung den Wert für die Spannung und den Widerstand. Der Zugriff auf diese Eigenschaften des Objekts wird durch die Methoden spannungLesen, spannungSetzen, widerstandLesen und widerstandSetzen bereitgestellt . Der lesende Zugriff auf die Eigenschaft Stromstärke wird durch die Methode stromLesen ermöglicht. Wie man sieht, wird das Ohm´sche Gesetz verwendet, um die Stromstärke zu berechnen.

Die Leistung wird in der Methode leistungBerechnen berechnet. In der Methode stromSetzen gehen wir davon aus, dass die Spannung gleich bleibt, und ändern den Widerstand der Leitung, den wir nach unseren Gleichungen berechnen .

Anschließend geben wir aus, wie viel Strom durch unsere Leitung bei 230 V fließt, wie groß der elektrische Widerstand ist und wie viel Leistung die Leitung verbraucht. Zum Schluss klemmen wir die Beispielleitung von der Batterie ab , um Energie zu sparen.

Falls Sie sich gefragt haben, was die Anfangszeilen des Skripts im mit markierten Abschnitt bedeuten: Je nachdem, in welchem Kontext es verwendet wird, stehen dem Skript unterschiedliche Objekte zur Verfügung. Die Funktion echo kann sowohl in einer DHTML-Seite als auch beim Ausführen des Skripts über den Windows Scripting Host verwendet werden.

Im folgenden Abschnitt sehen Sie nun, welche Rolle Operationen spielen, wenn die Verantwortlichkeit eines Objekts festgelegt wird.


Rheinwerk Computing - Zum Seitenanfang

4.1.3 Kontrakte: Ein Objekt trägt Verantwortung  Zur nächsten ÜberschriftZur vorigen Überschrift

Sie haben gesehen, welche Rolle Operationen und Methoden spielen, wenn die Schnittstelle eines Objekts festgelegt wird. Wir werden in diesem Abschnitt kurz darauf eingehen, wie denn für eine solche Operation sichergestellt werden kann, dass Objekte diese auch sinnvoll umsetzen.

Methoden und Bedingungen

Intuitiv ist klar, dass ein Objekt, das eine Operation unterstützt, diese auch inhaltlich sinnvoll durchführen muss. Das heißt also, die Methode, welche die Operation umsetzt, muss bestimmten Bedingungen genügen, sonst würden wir nicht davon sprechen, dass die Operation wirklich unterstützt wird.

Betrachten wir ein Beispiel dafür. Wenn ein Objekt eine Operation ueberweisen(Ausgangskonto, Zielkonto, Betrag) anbietet, die zwei Kontonummern und einen Betrag als Werte übergeben bekommt, erwarten wir, dass der Betrag vom ersten Konto auf das zweite umgebucht wird. Wenn wider Erwarten in solch einem Fall der Betrag von beiden Konten abgebucht würde, könnten Sie wohl kaum von einer korrekten Unterstützung der Operation durch das Objekt sprechen.

Wie können Sie nun aber festlegen und prüfen, ob die Umsetzung einer Operation das einhält, was sie verspricht? Helfen kann hier zumindest eine grundlegende Übereinkunft, dass Aufrufer und Aufgerufener einen Vertrag, einen sogenannten Kontrakt eingehen.


Icon Hinweis Kontrakt (Vertrag) bezüglich einer Operation

Objekte gehen einen Kontrakt ein, der die Rahmenbedingungen beim Aufruf einer Operation regelt.

Eine Operation hat Vorbedingungen, für deren Einhaltung der Aufrufer zu sorgen hat. Außerdem hat die Operation Nachbedingungen, für deren Einhaltung das aufgerufene Objekt verantwortlich ist. Zusätzlich können für ein Objekt Invarianten definiert werden. Diese sind unveränderliche Bedingungen, die für das Objekt immer gelten sollen.

Mehr zu Vorbedingungen, Nachbedingungen und Invarianten finden Sie in Abschnitt 4.2.2.


Bedeutung von Kontrakten

Diese Definition sieht zunächst einmal so aus, als würde sie nur wiederholen, was für gute Bibliotheken mit klar definierten Anwendungsschnittstellen ebenfalls gilt: Es muss gut dokumentiert werden, was ein Aufrufer an Werten zu übergeben hat und was eine Anwendungsschnittstelle daraufhin an Leistung anbietet.

Tatsächlich ist der Abschluss von Verträgen zwischen einem Aufrufer und einem Aufgerufenen nichts, was nur mit objektorientierter Programmierung möglich wäre. Der Kontrakt wird hier aber sehr wichtig, weil wir über die Interna von Objekten bei einem Aufruf nichts wissen. Das Einzige, auf das wir uns verlassen können, sind die Zusicherungen, die ein Objekt für seine Operationen und seine Eigenschaften macht.

Werfen Sie noch einmal einen Blick auf das Beispiel unserer elektrischen Leitung.

Abbildung 4.8    Eigenschaften von elektrischen Leitungen

Für das Objekt, das die elektrische Leitung repräsentiert, gelten die fachlichen Anforderungen, dass die Eigenschaften immer die aufgeführten Gleichungen erfüllen und dass der Widerstand und die Leistung immer positiv sein werden. In Abbildung 4.8 sind die entsprechenden Eigenschaften mit Bezug zur physikalischen Beschreibung von idealen Leitungen dargestellt.

Ein Objekt muss sicherstellen, dass die aufgeführten Gleichungen gelten. Dies ist Bestandteil des Kontrakts, den das Objekt mit anderen Objekten schließt.

In Abschnitt 4.2.2 werden wir genauer auf Kontrakte eingehen und diese dann auch für Klassen von Objekten formulieren. Dort werden wir auch Vorbedingungen, Nachbedingungen und Invarianten als Bestandteile eines Kontrakts beschreiben.


Rheinwerk Computing - Zum Seitenanfang

4.1.4 Die Identität von Objekten  Zur nächsten ÜberschriftZur vorigen Überschrift

Ein Objekt hat immer seine eigene Identität. Mehrere Objekte können die gleichen Daten besitzen (also gleich sein), und dennoch können sie unterschieden werden, weil jedes der Objekte eine eigene, unterscheidbare Identität besitzt.


Icon Hinweis Identität von Objekten

Objekte haben immer eine eigene Identität. Zwei Objekte können dadurch immer unterschieden werden, auch wenn sie zu einem Zeitpunkt exakt den gleichen Zustand aufweisen. Das Kriterium, nach dem Objekte grundsätzlich unterschieden werden können, wird Identitätskriterium genannt.

In vielen Programmiersprachen ist die Adresse des Objekts im Speicherbereich das generell verfügbare Identitätskriterium. In objektorientierten Anwendungen können auch andere Kriterien verwendet werden, wie zum Beispiel die Übereinstimmung einer eindeutigen Kennung.


Unterscheidung Objekte und Werte

Der Besitz der Identität ist etwas, was Objekte von Werten, also zum Beispiel Zahlen oder Datumsangaben, unterscheidet. Bei Werten stellt sich die Frage nach der Identität nicht, interessant ist nur die Gleichheit. Wenn Sie die Zahl 6 durch 2 teilen, erhalten Sie eine 3. Dabei ist es irrelevant, »welche Hälfte« von der 6 Sie als Ergebnis bekommen haben, denn die Zahl 3 ist ein Wert, der keine Identität besitzt. Alle Zahlen 3 sind gleich und austauschbar.

Wenn Sie allerdings von sechs Dateien drei löschen, können Sie durchaus sagen, welche der sechs Dateien gelöscht worden sind – und das können Sie auch dann, wenn alle sechs Dateien den gleichen Inhalt haben. Sie können z. B. durch ihre Position im Dateisystem unterschieden werden.

JavaScript und Identität

Betrachten Sie die Frage der Identität noch einmal anhand unseres JavaScript-Beispiels mit der elektrischen Leitung. Wir verwenden diesmal mehr als eine elektrische Leitung. In Listing 4.2 ist die Umsetzung in JavaScript gezeigt, in Abbildung 4.9 findet sich die Darstellung der resultierenden Referenzen.

elektrischeLeitung = new Object; 
elektrischeLeitung.spannungSetzen(230.0);  
elektrischeLeitung.widerstandSetzen(1000.0);  
 
dieselbeLeitung = elektrischeLeitung;  
andereLeitung = new Object; 
andereLeitung.spannungSetzen(230.0);  
andereLeitung.widerstandSetzen(1000.0); 
dieselbeLeitung.spannungSetzen(110.0);  
alert(elektrischeLeitung.spannungLesen());

Listing 4.2    Mehrere Referenzen auf dasselbe Objekt

Abbildung 4.9    Mehrere Variablen referenzieren dasselbe Objekt.

In den mit markierten Zeilen werden dem Objekt elektrischeLeitung seine Werte zugeordnet. Dabei ist der Wert 230.0 einfach ein Wert, der sich in nichts von dem Wert 230.0 unterscheiden kann, der in Zeile erneut verwendet wird. Dagegen hat das Objekt elektrischeLeitung eine Identität und kann deshalb auch auf verschiedene Weise referenziert werden. Die beiden Variablen elektrischeLeitung und dieselbeLeitung verweisen aufgrund der Zuweisung in Zeile auf dasselbe Objekt, also eine elektrische Leitung, wie auch aus Abbildung 4.9 deutlich wird. Wenn wir nun für dieselbeLeitung die Spannung heruntersetzen (Zeile ), wird auch die Abfrage der Spannung für elektrischeLeitung den geänderten Wert liefern.

In objektorientierten Sprachen werden häufig auch Werte als Objekte abgebildet – das heißt, Datenstrukturen, die keine fachliche Identität haben, werden als Objekte gehandhabt und bekommen technisch bedingt eine Identität. Wenn Sie also Objekte vergleichen möchten, sollten Sie immer darauf achten, ob Sie die Gleichheit, also den Wert, oder die Identität vergleichen möchten.

Mit der Frage der Identität von Objekten befassen wir uns ausführlicher in Abschnitt 4.4.4.


Rheinwerk Computing - Zum Seitenanfang

4.1.5 Objekte haben Beziehungen  topZur vorigen Überschrift

Ein Objekt steht in der Anwendung nicht allein, sondern arbeitet mit anderen Objekten zusammen. Dabei stellt es den anderen Objekten seine eigene Funktionalität zur Verfügung und nutzt die Funktionalität anderer Objekte. Ein Objekt hat also Beziehungen zu anderen Objekten, die selbst eine eigene Identität haben.

Bei der Beschreibung der Beziehung können wir der Beziehung einen Namen geben, oder wir können zusätzlich oder stattdessen die Rollen der Objekte in der Beziehung benennen. Abbildung 4.10 zeigt einige wohl bekannte Objekte mit ihren Beziehungen.

Abbildung 4.10    Beziehungen zwischen den Objekten der antiken Mythologie

Wir werden uns die verschiedenen Beziehungen zwischen Objekten in Abschnitt 4.3 genauer anschauen. Zunächst werden wir uns aber mit einem weiteren grundlegenden Konzept der Objektorientierung beschäftigen: mit den Klassen.



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

 <<   zurück
  Zum Rheinwerk-Shop
Neuauflage: Objektorientierte Programmierung






Neuauflage:
Objektorientierte Programmierung

Jetzt Buch bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: Einstieg in Python






 Einstieg in Python


Zum Rheinwerk-Shop: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern