Lektion 1.2

Objektorientierte Modellierung und Programmierung

21 min Lesezeit

Eine Schulbibliothek verwaltet Bücher und Leser. Ein Buch hat einen Titel und ist verliehen oder nicht, die Bibliothek kennt ihre Bücher, ein Leser leiht Bücher aus. Wer das Programm später erweitern muss, braucht eine Gliederung: Welche Klassen gibt es? Wer darf welche Daten ändern? Wie hängen die Klassen zusammen, und wie hält man das fest, bevor man programmiert?

Du lernst, ein System aus Objekten zu entwerfen: Referenzen, Kapselung, Beziehungen zwischen Klassen, UML-Diagramme und Regeln für einen guten Entwurf. Die Beispiele sind in Python.

Definition

Objekt, Klasse, Attribut, Methode und Konstruktor

Ein Objekt hat einen Zustand (die Werte seiner Attribute), ein Verhalten (seine Methoden) und eine Identität: Zwei Objekte sind verschieden, auch wenn alle Attributwerte gleich sind. Eine Klasse legt Attribute und Methoden für alle ihre Objekte fest. Der Konstruktor läuft beim Erzeugen eines Objekts und setzt den Anfangszustand. In Python heißt er __init__\texttt{\_\_init\_\_}, und self\texttt{self} ist das Objekt, das die Methode aufgerufen hat.

ZeileEinrückungAnweisung
10class Buch:\texttt{class Buch:}
21def __init__(self, titel):\texttt{def \_\_init\_\_(self, titel):}
32self.titel = titel\texttt{self.titel = titel}
42self._verliehen = False\texttt{self.\_verliehen = False}
51def ist_verliehen(self):\texttt{def ist\_verliehen(self):}
62return self._verliehen\texttt{return self.\_verliehen}
71def ausleihen(self):\texttt{def ausleihen(self):}
82if self._verliehen:\texttt{if self.\_verliehen:}
93return False\texttt{return False}
102self._verliehen = True\texttt{self.\_verliehen = True}
112return True\texttt{return True}

b = Buch("Faust")\texttt{b = Buch("Faust")} erzeugt ein Objekt. b.ausleihen()\texttt{b.ausleihen()} sendet ihm die Nachricht ausleihen\texttt{ausleihen}.

Definition

Referenz

Eine Variable, die ein Objekt trägt, enthält nur eine Referenz, einen Verweis auf das Objekt. c = b\texttt{c = b} kopiert den Verweis, nicht das Objekt (Aliasing): Beide Namen führen zum selben Objekt. None\texttt{None} bedeutet „kein Objekt“, und None.ausleihen()\texttt{None.ausleihen()} bricht mit einem Fehler ab. Der Operator is\texttt{is} prüft, ob es dasselbe Objekt ist, ==\texttt{==} prüft Gleichheit, die eine Klasse selbst festlegen kann.

Definition

Zugriffsmodifikatoren, Kapselung und Geheimnisprinzip

Ein Zugriffsmodifikator legt fest, wer ein Attribut oder eine Methode benutzen darf:

SichtbarkeitUMLJavaPython
öffentlich+publickein Unterstrich
geschützt (Klasse und Unterklassen)#protectedein Unterstrich, nur Vereinbarung
privat (nur die Klasse selbst)-privatezwei Unterstriche (Namensverschleierung), kein echter Schutz

Kapselung heißt: Der Zustand eines Objekts ist privat, Zugriffe laufen über öffentliche Methoden. Das Geheimnisprinzip geht weiter: Nutzer kennen nur die Schnittstelle, also die öffentlichen Methoden und was sie leisten, nicht die innere Darstellung. Dann lässt sich die Darstellung ändern, etwa ein Guthaben statt in Euro in Cent speichern, ohne fremden Code anzupassen. Eine Getter-Methode liest ein Attribut, eine Setter-Methode setzt es und kann Werte prüfen, bevor sie gespeichert werden. Wer aber für jedes Attribut einen Getter und einen Setter anbietet, gibt die innere Darstellung doch preis.

Definition

Assoziation, Aggregation und Komposition

Die Multiplizität am Ende einer Beziehung gibt an, wie viele Objekte beteiligt sind: 1\texttt{1} genau eins, 0..1\texttt{0..1} höchstens eins, *\texttt{*} beliebig viele (auch keins), 1..*\texttt{1..*} mindestens eins.

BeziehungBedeutungUMLBeispiel
AssoziationObjekte kennen oder nutzen einanderLinie mit MultiplizitätenLeser 0..1 und Buch *: Ein Buch ist an höchstens einen Leser verliehen, ein Leser hat beliebig viele Bücher
AggregationTeil und Ganzes, die Teile gibt es auch ohne das GanzeLinie mit hohler Raute am GanzenBibliothek und Buch: Ein Buch kann in eine andere Bibliothek wechseln
KompositionTeil und Ganzes, der Teil gehört genau einem Ganzen und endet mit ihmLinie mit ausgefüllter Raute am GanzenBuch und Kapitel: Ohne das Buch gibt es seine Kapitel nicht mehr

In Python steht eine Beziehung als Attribut, das auf Objekte verweist, zum Beispiel _buecher\texttt{\_buecher} in der Bibliothek.

Definition

UML-Klassendiagramm und Sequenzdiagramm

Ein Klassendiagramm zeigt jede Klasse als Rechteck mit drei Fächern (Name, Attribute, Methoden) und die Beziehungen zwischen den Klassen. Einträge schreibt man als - verliehen : bool\texttt{- verliehen : bool} oder + ausleihen() : bool\texttt{+ ausleihen() : bool}. Die Klasse Buch hat also + titel : str\texttt{+ titel : str}, - verliehen : bool\texttt{- verliehen : bool}, + ist_verliehen() : bool\texttt{+ ist\_verliehen() : bool} und + ausleihen() : bool\texttt{+ ausleihen() : bool}. Von Bibliothek führt eine Linie mit hohler Raute zu Buch, am Buch-Ende steht *\texttt{*}.

Ein Sequenzdiagramm zeigt, in welcher Reihenfolge Objekte einander Nachrichten senden. Jedes Objekt hat oben ein Rechteck name : Klasse\texttt{name : Klasse} und darunter eine senkrechte Lebenslinie. Die Zeit läuft von oben nach unten. Ein Pfeil zwischen zwei Lebenslinien ist ein Methodenaufruf, ein gestrichelter Pfeil zurück ist die Rückgabe.

Definition

Objekte in Feldern und Listen

Objekte lassen sich in einem Feld (feste Länge, Zugriff über den Index) oder einer Liste (veränderliche Länge) sammeln. Das Feld enthält Referenzen, nicht die Objekte selbst. Eine Schleife sendet jedem Element dieselbe Nachricht. Verändert man ein Objekt über die Schleifenvariable, ändert man das Objekt in der Liste, denn beide verweisen auf dasselbe.

ZeileEinrückungAnweisung
120class Bibliothek:\texttt{class Bibliothek:}
131def __init__(self):\texttt{def \_\_init\_\_(self):}
142self._buecher = []\texttt{self.\_buecher = []}
151def aufnehmen(self, buch):\texttt{def aufnehmen(self, buch):}
162self._buecher.append(buch)\texttt{self.\_buecher.append(buch)}
171def zaehle_verfuegbar(self):\texttt{def zaehle\_verfuegbar(self):}
182n = 0\texttt{n = 0}
192for b in self._buecher:\texttt{for b in self.\_buecher:}
203if not b.ist_verliehen():\texttt{if not b.ist\_verliehen():}
214n = n + 1\texttt{n = n + 1}
222return n\texttt{return n}
Beispiel

Bibliothek: Ablauf und Sequenz

Es gilt b1 = Buch("Faust")\texttt{b1 = Buch("Faust")}, b2 = Buch("Momo")\texttt{b2 = Buch("Momo")} und bib = Bibliothek()\texttt{bib = Bibliothek()}. Dann laufen bib.aufnehmen(b1)\texttt{bib.aufnehmen(b1)}, bib.aufnehmen(b2)\texttt{bib.aufnehmen(b2)}, b1.ausleihen()\texttt{b1.ausleihen()}, ok = b1.ausleihen()\texttt{ok = b1.ausleihen()} und print(bib.zaehle_verfuegbar(), ok)\texttt{print(bib.zaehle\_verfuegbar(), ok)}. Was wird ausgegeben, und wie läuft der letzte Aufruf als Sequenz ab?

Das erste b1.ausleihen()\texttt{b1.ausleihen()} setzt verliehen auf True und gibt True zurück. Das zweite findet verliehen schon True vor und gibt False zurück, also ist ok gleich False. Beim Zählen sendet die Bibliothek jedem Buch der Liste eine Nachricht:

Nr.SenderEmpfängerNachricht
1Aufruferbib : Bibliothekzaehle_verfuegbar()\texttt{zaehle\_verfuegbar()}
2bibb1 : Buchist_verliehen()\texttt{ist\_verliehen()}
3b1bibRückgabe True
4bibb2 : Buchist_verliehen()\texttt{ist\_verliehen()}
5b2bibRückgabe False
6bibAufruferRückgabe 1

Nur b2 ist nicht verliehen, also ist n gleich 1. Die Ausgabe ist 1 False.

Definition

Klassenentwurf: Kohäsion und Kopplung

Zwei Maße beurteilen einen Entwurf. Die Kohäsion misst, wie eng die Aufgaben innerhalb einer Klasse zusammengehören. Sie soll hoch sein: Eine Klasse hat eine klar umrissene Verantwortung. Die Kopplung misst, wie stark Klassen voneinander abhängen. Sie soll lose sein: Eine Klasse kennt wenige andere und nur deren Schnittstelle.

Eine Klasse Konto, die auch Kontoauszüge formatiert und E-Mails verschickt, hat niedrige Kohäsion, und jede Änderung am Mailversand zwingt zum Ändern von Konto. Besser sind getrennte Klassen für Konto, Kontoauszug und Mailversand. Greift eine Klasse direkt auf Attribute einer anderen zu, ist die Kopplung eng: Jede Änderung der inneren Darstellung wirkt sich dort aus.

Häufige Fehler

Eine Liste mit Malzeichen füllen

„buecher = [Buch("A")] * 3\texttt{buecher = [Buch("A")] * 3} erzeugt drei Bücher.“

Richtig ist

Der Konstruktor läuft nur einmal. Die Liste enthält drei Verweise auf dasselbe Objekt, wer eines ausleiht, hat alle ausgeliehen. Drei Objekte entstehen nur durch drei Konstruktoraufrufe, etwa in einer Schleife mit append\texttt{append}.

Häufige Fehler

Kapselung bedeutet Getter und Setter für alles

„Kapselung heißt: Alle Attribute sind privat, und jedes bekommt einen Getter und einen Setter.“

Richtig ist

Dann ist die innere Darstellung über die Methoden wieder frei zugänglich, und das Geheimnisprinzip ist verletzt. Eine gekapselte Klasse bietet Operationen an, die zu ihrer Aufgabe passen, etwa ausleihen()\texttt{ausleihen()}.

Zusammenfassung

  • Ein Objekt hat Zustand, Verhalten und Identität. Der Konstruktor setzt den Anfangszustand.

  • Eine Objektvariable enthält eine Referenz. Eine Zuweisung kopiert den Verweis, is\texttt{is} prüft Identität.

  • Kapselung: Zustand privat, Zugriff über Methoden. Geheimnisprinzip: Nutzer kennen nur die Schnittstelle.

  • Assoziation verbindet Objekte. Aggregation und Komposition sind Teil-Ganzes-Beziehungen, bei der Komposition endet der Teil mit dem Ganzen.

  • Das Klassendiagramm zeigt Klassen, Beziehungen und Multiplizitäten, das Sequenzdiagramm die Folge der Nachrichten.

  • Felder und Listen enthalten Referenzen auf Objekte.

  • Guter Entwurf: hohe Kohäsion in den Klassen, lose Kopplung zwischen ihnen.

Übungen

Aufgabe 1Numerisch

Die Bibliothek enthält die vier Bücher b1, b2, b3 und b4. Es laufen b1.ausleihen()\texttt{b1.ausleihen()}, b2.ausleihen()\texttt{b2.ausleihen()}, b1.ausleihen()\texttt{b1.ausleihen()} und b4.ausleihen()\texttt{b4.ausleihen()}. Welche Zahl gibt bib.zaehle_verfuegbar()\texttt{bib.zaehle\_verfuegbar()} danach zurück?

Aufgabe 2Multiple Choice

Ein Gebäude besteht aus Räumen. Wird das Gebäude abgerissen, gibt es die Räume nicht mehr. Wie modelliert man das?

Aufgabe 3Multiple Choice

Welche Klasse hat hohe Kohäsion?

Aufgabe 4Numerisch

Es läuft a = [Buch("A")] * 3\texttt{a = [Buch("A")] * 3}, dann a[0].ausleihen()\texttt{a[0].ausleihen()}. Bei wie vielen der drei Listeneinträge liefert ist_verliehen()\texttt{ist\_verliehen()} danach False?

Aufgabe 5Freitext

Die Klasse Schueler hat ein öffentliches Attribut note. Ein anderes Programmteil setzt s.note = 17\texttt{s.note = 17}, obwohl nur 1 bis 6 gültig sind. Erkläre, wie Kapselung das verhindert, und gib die Methode an.