Lektion 1.3

Objektorientierte Modellierung, Einstieg

20 min Lesezeit

Eine Banking-App verwaltet Tausende Konten. Jedes hat einen Inhaber und ein Guthaben, und auf jedem kann man Geld einzahlen oder überweisen. Für jedes Konto eigene Variablen und eigene Programmteile zu schreiben, ginge nicht lange gut.

Die objektorientierte Programmierung löst das so: Du beschreibst einmal, wie ein Konto aufgebaut ist und was es kann, und erzeugst daraus beliebig viele Konten. Du lernst die Begriffe dafür und wie man den Entwurf zeichnet. Die Beispiele sind in Python geschrieben.

Definition

Objekt, Klasse, Attribut und Methode

Ein Objekt ist ein einzelnes Ding mit eigenen Daten und Fähigkeiten, zum Beispiel das Konto von Mia. Seine Daten heißen Attribute: Inhaber ist "Mia", Guthaben ist 50. Was es kann, heißt Methode: einzahlen, das Guthaben nennen.

Eine Klasse ist der Bauplan für Objekte gleicher Art, wie eine Backform für Kekse. Sie legt fest, welche Attribute und Methoden die Objekte haben. Ein Objekt heißt auch Instanz seiner Klasse. Die Klasse Konto gibt es einmal, Konten als Objekte beliebig oft, jedes mit eigenen Attributwerten.

Definition

Klasse und Konstruktor in Python

Ein Konstruktor ist eine besondere Methode, die beim Erzeugen eines Objekts läuft und die Attribute auf Startwerte setzt. In Python heißt er __init__\texttt{\_\_init\_\_}. Der Parameter self\texttt{self} steht für das Objekt, das gerade bearbeitet wird.

ZeileEinrückungAnweisung
10class Konto:\texttt{class Konto:}
21def __init__(self, inhaber, guthaben):\texttt{def \_\_init\_\_(self, inhaber, guthaben):}
32self.inhaber = inhaber\texttt{self.inhaber = inhaber}
42self._guthaben = guthaben\texttt{self.\_guthaben = guthaben}
51def einzahlen(self, betrag):\texttt{def einzahlen(self, betrag):}
62self._guthaben = self._guthaben + betrag\texttt{self.\_guthaben = self.\_guthaben + betrag}
71def guthaben(self):\texttt{def guthaben(self):}
82return self._guthaben\texttt{return self.\_guthaben}

Ein Objekt erzeugst du mit dem Namen der Klasse: mia = Konto("Mia", 50)\texttt{mia = Konto("Mia", 50)} ruft den Konstruktor auf. Methoden rufst du mit Punkt auf: mia.einzahlen(30)\texttt{mia.einzahlen(30)}. Der Unterstrich vor guthaben\texttt{guthaben} hat einen Grund, den du bei der Datenkapselung erfährst. In anderen Sprachen wie Java trägt der Konstruktor den Namen der Klasse.

Definition

Klassen als Datentypen und Referenzen

Eine Klasse ist ein selbst definierter Datentyp. Konto\texttt{Konto} steht neben int\texttt{int} und str\texttt{str}, und eine Variable kann ein Konto tragen. Die Variable enthält aber nicht das Objekt selbst, sondern eine Referenz, also einen Verweis darauf, wie eine Adresse auf einem Zettel.

Darum kopiert zweiter = mia\texttt{zweiter = mia} nur den Verweis. Danach zeigen beide Namen auf dasselbe Objekt, und eine Änderung über den einen Namen sieht man über den anderen. Ein neues Objekt entsteht nur, wenn du den Konstruktor aufrufst.

Definition

Kommunikation zwischen Objekten

Objekte arbeiten zusammen, indem eines eine Methode des anderen aufruft. Man sagt, es sendet ihm eine Nachricht. Wir ergänzen die Klasse Konto um eine Methode, die Geld an ein anderes Konto überweist (Umlaute lassen wir in Namen weg).

ZeileEinrückungAnweisung
91def ueberweisen(self, ziel, betrag):\texttt{def ueberweisen(self, ziel, betrag):}
102if betrag <= self._guthaben:\texttt{if betrag <= self.\_guthaben:}
113self._guthaben = self._guthaben - betrag\texttt{self.\_guthaben = self.\_guthaben - betrag}
123ziel.einzahlen(betrag)\texttt{ziel.einzahlen(betrag)}

Das Konto bucht den Betrag bei sich ab und sendet dem Zielkonto die Nachricht einzahlen\texttt{einzahlen}. Ist zu wenig Guthaben da, passiert nichts.

Beispiel

Objekte, Referenz und Überweisung

Welche drei Zahlen gibt dieses Programm aus?

ZeileEinrückungAnweisung
10mia = Konto("Mia", 50)\texttt{mia = Konto("Mia", 50)}
20ben = Konto("Ben", 20)\texttt{ben = Konto("Ben", 20)}
30mia.einzahlen(30)\texttt{mia.einzahlen(30)}
40zweiter = mia\texttt{zweiter = mia}
50zweiter.einzahlen(10)\texttt{zweiter.einzahlen(10)}
60mia.ueberweisen(ben, 25)\texttt{mia.ueberweisen(ben, 25)}
70print(mia.guthaben(), ben.guthaben(), zweiter.guthaben())\texttt{print(mia.guthaben(), ben.guthaben(), zweiter.guthaben())}

Zeile 1 und 2 erzeugen zwei Objekte: Mia hat 50, Ben 20. Zeile 3 ruft einzahlen\texttt{einzahlen} nur für Mias Objekt auf: 50 + 30 = 80. Zeile 4 erzeugt kein neues Konto, zweiter\texttt{zweiter} zeigt auf Mias Objekt. Zeile 5 zahlt dort 10 ein: 80 + 10 = 90.

Zeile 6: 25 ist höchstens so groß wie 90, also wird gebucht. Mias Objekt hat danach 90 - 25 = 65, und Bens Objekt bekommt 20 + 25 = 45. Zeile 7 gibt 65, 45 und 65 aus. Mia und zweiter\texttt{zweiter} sind dasselbe Objekt, darum stehen dort gleiche Werte.

Definition

Datenkapselung

Datenkapselung heißt: Die Attribute eines Objekts sind von außen nicht frei zugänglich, man erreicht sie nur über Methoden. Die Methoden achten darauf, dass die Daten gültig bleiben. ueberweisen\texttt{ueberweisen} bucht nur ab, wenn genug Guthaben da ist. Könnte jedes Programmteil mia._guthaben\texttt{mia.\_guthaben} beliebig setzen, ließe sich diese Regel umgehen.

Python erzwingt das nicht. Ein Unterstrich vor dem Namen ist eine Vereinbarung: "Nicht von außen anfassen". Sprachen wie Java sperren den Zugriff mit dem Schlüsselwort private\texttt{private}.

Definition

UML-Klassendiagramm und Objektdiagramm

Entwürfe zeichnet man mit der UML (Unified Modeling Language), einer genormten Zeichensprache. Ein Klassendiagramm zeigt eine Klasse als Rechteck mit drei Fächern: Name, Attribute, Methoden. Ein Objektdiagramm zeigt ein einzelnes Objekt: oben "objektname : Klassenname" (unterstrichen), darunter die Attribute mit ihren Werten. Methoden fehlen, sie stehen in der Klasse. Ein Plus vor einem Eintrag heißt öffentlich, ein Minus heißt privat.

FachKlassendiagrammObjektdiagramm
KopfKontomia : Konto
Attribut+ inhaber : strinhaber = "Mia"
Attribut- guthaben : intguthaben = 50
Methode+ einzahlen(betrag)fehlt
Methode+ ueberweisen(ziel, betrag)fehlt
Methode+ guthaben()fehlt
Definition

Generalisierung, Spezialisierung, Vererbung und Polymorphismus

Hund und Katze haben beide einen Namen und können einen Laut von sich geben. Die Generalisierung fasst solches Gemeinsame in einer allgemeineren Oberklasse zusammen, hier Tier. Umgekehrt ist Hund eine Spezialisierung von Tier: eine Unterklasse, die mehr kann oder etwas anders macht.

Bei der Vererbung übernimmt die Unterklasse alle Attribute und Methoden der Oberklasse. Sie kann neue hinzufügen und geerbte überschreiben. In Python steht die Oberklasse in Klammern: class Hund(Tier):\texttt{class Hund(Tier):}. Zwischen beiden gilt die "ist ein"-Beziehung: Ein Hund ist ein Tier. Im UML-Diagramm führt ein Pfeil mit hohler Dreiecksspitze von der Unterklasse zur Oberklasse.

ZeileEinrückungAnweisung
10class Tier:\texttt{class Tier:}
21def __init__(self, name):\texttt{def \_\_init\_\_(self, name):}
32self.name = name\texttt{self.name = name}
41def laut(self):\texttt{def laut(self):}
52return "..."\texttt{return "..."}
60class Hund(Tier):\texttt{class Hund(Tier):}
71def laut(self):\texttt{def laut(self):}
82return "Wuff"\texttt{return "Wuff"}
90class Katze(Tier):\texttt{class Katze(Tier):}
101def laut(self):\texttt{def laut(self):}
112return "Miau"\texttt{return "Miau"}

Hund und Katze erben den Konstruktor und das Attribut name\texttt{name} und überschreiben laut\texttt{laut}. Polymorphismus (Vielgestaltigkeit) heißt: Derselbe Aufruf bewirkt je nach Klasse des Objekts etwas anderes.

Beispiel

Derselbe Aufruf, anderes Verhalten

Zu den Klassen oben gehört dieses Programm:

ZeileEinrückungAnweisung
120tiere = [Hund("Rex"), Katze("Luna"), Tier("Fisch")]\texttt{tiere = [Hund("Rex"), Katze("Luna"), Tier("Fisch")]}
130for t in tiere:\texttt{for t in tiere:}
141print(t.name, t.laut())\texttt{print(t.name, t.laut())}

Was wird ausgegeben?

Die Schleife ruft für jedes Element t.laut()\texttt{t.laut()} auf, ohne die Klasse zu kennen. Python wählt die Methode passend zum Objekt: Beim Hund liefert sie "Wuff", bei der Katze "Miau", und das Tier ohne eigene Version nutzt die Methode der Oberklasse und liefert "...". Die Ausgabe lautet: Rex Wuff, Luna Miau, Fisch ...

Häufige Fehler

Klasse und Objekt verwechseln

„Konto ist ein Objekt, und Mias Konto ist die Klasse.“

Richtig ist

Es ist umgekehrt. Konto\texttt{Konto} ist die Klasse, der Bauplan. Mias Konto ist ein Objekt, eine Instanz dieser Klasse. Aus einer Klasse entstehen beliebig viele Objekte.

Häufige Fehler

Eine Zuweisung erzeugt ein neues Objekt

„Mit zweiter = mia\texttt{zweiter = mia} habe ich ein zweites Konto mit demselben Guthaben.“

Richtig ist

Es gibt weiterhin nur ein Objekt, und zwei Namen zeigen darauf. Ein zweites Konto entsteht nur mit einem neuen Aufruf des Konstruktors, etwa Konto("Mia", 50)\texttt{Konto("Mia", 50)}.

Häufige Fehler

Vererbung bei "hat ein"

„Ein Auto hat Reifen, also ist Reifen eine Oberklasse von Auto.“

Richtig ist

Vererbung passt nur bei "ist ein", wie bei Hund und Tier. Dass ein Auto Reifen hat, ist keine Vererbung. Die Klasse Auto bekommt dafür ein Attribut, das auf Reifen-Objekte verweist.

Zusammenfassung

  • Ein Objekt hat Attribute (Daten) und Methoden (Fähigkeiten). Eine Klasse ist der Bauplan, ein Objekt ist eine Instanz.

  • Der Konstruktor setzt beim Erzeugen die Startwerte. In Python heißt er __init__\texttt{\_\_init\_\_}.

  • Eine Klasse ist ein Datentyp. Eine Variable enthält nur eine Referenz auf das Objekt, eine Zuweisung kopiert den Verweis.

  • Objekte kommunizieren, indem eines Methoden des anderen aufruft.

  • Datenkapselung: Attribute sind nur über Methoden erreichbar, damit die Daten gültig bleiben.

  • Eine Unterklasse erbt von der Oberklasse und kann überschreiben. Polymorphismus heißt, derselbe Aufruf wirkt je nach Klasse anders.

  • Klassendiagramm: Name, Attribute, Methoden. Objektdiagramm: Objektname, Klasse und Attributwerte.

Übungen

Aufgabe 1Multiple Choice

Was ist eine Klasse?

Aufgabe 2Numerisch

Es läuft a = Konto("Tom", 40)\texttt{a = Konto("Tom", 40)}, dann b = a\texttt{b = a}, dann b.einzahlen(15)\texttt{b.einzahlen(15)} und dann a.einzahlen(5)\texttt{a.einzahlen(5)}. Welche Zahl gibt a.guthaben()\texttt{a.guthaben()} aus?

Aufgabe 3Numerisch

Es gilt x = Konto("A", 100)\texttt{x = Konto("A", 100)} und y = Konto("B", 10)\texttt{y = Konto("B", 10)}. Dann läuft x.ueberweisen(y, 30)\texttt{x.ueberweisen(y, 30)} und danach x.ueberweisen(y, 90)\texttt{x.ueberweisen(y, 90)}. Welche Zahl gibt y.guthaben()\texttt{y.guthaben()} am Ende aus?

Aufgabe 4Multiple Choice

Für welches Paar ist Vererbung sinnvoll?

Aufgabe 5Freitext

In der Klasse Konto steht das Guthaben nur in _guthaben\texttt{\_guthaben}, und ueberweisen\texttt{ueberweisen} prüft vor dem Abbuchen den Kontostand. Erkläre, welches Problem die Datenkapselung hier verhindert.