Informatik

Objektorientierung: Klassen, Objekte und Polymorphie

Objektorientierung: Klassen, Objekte und Polymorphie
Objektorientierung: Klassen, Objekte und Polymorphie
Für Quiz, Lückentext, Lernkarten und Fortschritt ist JavaScript nötig. Alle Inhalte und Lösungen bleiben direkt lesbar.

Bei der Objektorientierung modellierst du ein Programm als Zusammenspiel eigenständiger Objekte. Jedes Objekt besitzt eine Identität, einen Zustand und ein Verhalten. Klassen beschreiben den gemeinsamen Aufbau ähnlicher Objekte.

Auf dieser Seite lernst du, ein kleines Objektmodell zu entwerfen, gekapselte Zustandsänderungen nachzuvollziehen und polymorphe Methodenaufrufe zu erklären.

Deine Lernziele

Hake ab, was du schon kannst — und komm am Ende hierher zurück!

Objekte verbinden Zustand und Verhalten

Stell dir zwei Lampen in einem Smart Home vor. Beide können ausgeschaltet sein und sehen im Programm gleich aus. Trotzdem sind es zwei verschiedene Objekte: Die Küchenlampe kann eingeschaltet werden, ohne dass sich die Flurlampe verändert.

Definition

Objekt

Ein Objekt ist eine eigenständige Programmeinheit mit Identität, Zustand und Verhalten. Es kann auch etwas Abstraktes wie ein Benutzerkonto oder eine Bestellung darstellen.

  • Die Identität unterscheidet das Objekt von allen anderen Objekten.
  • Der Zustand ergibt sich aus seinen aktuellen Attributwerten.
  • Das Verhalten wird durch Methoden beschrieben.

Bei einer Lampe kann das Attribut an den Zustand speichern. Methoden wie einschalten() und ausschalten() verändern diesen Zustand.

Merke

Gleicher Zustand bedeutet nicht gleiche Identität. Zwei ausgeschaltete Lampen bleiben zwei verschiedene Objekte.

Teste dich
Frage 1 von 2LeichtWelche Aussage beschreibt ein Objekt vollständig?
Lösung: Es besitzt eine Identität, einen Zustand und ein Verhalten. — Eine Klasse beschreibt einen gemeinsamen Typ. Ein Objekt ist eine konkrete Einheit dieses Typs mit eigener Identität und eigenem Zustand.
Frage 2 von 2MittelZwei Lampen besitzen beide den Zustand an = false. Was folgt daraus?
Lösung: Sie können trotzdem verschiedene Objekte sein. — Der Zustand kann gleich sein, während die Identitäten verschieden bleiben.
Von der Klasse zur konkreten Instanz

In klassenbasierten Sprachen legt eine Klasse fest, welche Attribute und Methoden ihre Objekte besitzen. Sie ist der gemeinsame Bauplan, aber noch nicht das einzelne Objekt.

Definition

Klasse

Eine Klasse beschreibt einen Objekttyp. Ein nach dieser Beschreibung erzeugtes Objekt heißt Instanz der Klasse. Das Erzeugen wird Instanziierung genannt.

Für eine Klasse Lampe könnte der gemeinsame Aufbau so aussehen:

  • Attribut: an
  • Konstruktor: Lampe()
  • Methoden: einschalten(), ausschalten() und istAn()

Der Konstruktor richtet ein neues Objekt ein. Er gibt ihm einen gültigen Anfangszustand. Bei der Lampe setzt der Konstruktor an auf false.

In Java erzeugt new Lampe() eine neue Instanz. Die Deklaration Lampe kueche; allein erzeugt noch keine Lampe. Sie stellt nur eine Referenzvariable bereit, die später auf ein Objekt verweisen kann.

Beispiel

Lampe kueche = new Lampe(); lässt sich in drei Gedanken zerlegen:

  1. new Lampe() erzeugt ein neues Lampenobjekt.
  2. Der Konstruktor setzt den Anfangszustand an = false.
  3. Die Referenzvariable kueche verweist anschließend auf dieses Objekt.

Ein Aufruf von kueche.einschalten() verändert den Zustand genau dieses Lampenobjekts zu an = true.

Lückentext

Wähle in jeder Lücke die passende Form und prüfe anschließend deine Antworten.

Eine beschreibt den gemeinsamen Aufbau. Ein konkretes Objekt dieser Klasse heißt . Der richtet dessen Anfangszustand ein.

Lösungen: Lücke 1: Klasse; Lücke 2: Instanz; Lücke 3: Konstruktor. Klasse, Instanz und Konstruktor erfüllen verschiedene Aufgaben: Beschreibung, konkretes Objekt und Initialisierung.
Kapselung schützt gültige Zustände

Wenn fremder Code jedes Attribut beliebig verändern dürfte, könnte ein Objekt leicht in einen ungültigen Zustand geraten. Kapselung begrenzt deshalb den direkten Zugriff.

Definition

Kapselung

Kapselung bedeutet, Zustand und zugehöriges Verhalten in einem Objekt zu bündeln. Andere Programmteile greifen über eine festgelegte Schnittstelle darauf zu, statt interne Attribute unkontrolliert zu verändern.

Für die Lampe kann an privat sein. Außenstehender Code nutzt dann einschalten(), ausschalten() oder istAn(). Die Klasse kontrolliert selbst, wie ihr Zustand gelesen oder geändert wird.

Ein vollständiger Zustandsweg sieht so aus:

  1. Der Konstruktor erzeugt eine ausgeschaltete Lampe.
  2. einschalten() setzt an auf true.
  3. istAn() liefert den aktuellen Zustand, verändert ihn aber nicht.
  4. ausschalten() setzt an wieder auf false.
Beispiel

Du modellierst ein Support-Ticket mit dem Zustand offen oder geschlossen. Das Attribut bleibt privat. Der Konstruktor erzeugt ein offenes Ticket. Die Methode schliessen() erlaubt den Wechsel zu geschlossen.

Eine freie Zuweisung von außen ist nicht nötig. Damit bleibt die Regel an einer Stelle gebündelt: Nur die Klasse entscheidet, wie sich der Ticketzustand ändert.

Teste dich
Frage 1 von 2MittelWarum sollte das Zustandsattribut des Tickets nicht beliebig von außen gesetzt werden?
Lösung: Die Klasse soll Zustandsänderungen über ihre Schnittstelle kontrollieren. — Eine gekapselte Klasse kann Regeln für gültige Zustandsänderungen an einer zentralen Stelle durchsetzen.
Frage 2 von 2SchwerEine neue Regel verbietet das Schließen eines bereits geschlossenen Tickets. Wo gehört diese Prüfung hin?
Lösung: In die Methode schliessen() des Tickets. — Das Objekt verwaltet seinen Zustand selbst. Deshalb prüft seine Methode, ob der Zustandswechsel zulässig ist.
Objekte arbeiten über Nachrichten zusammen

Ein objektorientiertes Programm besteht meist nicht aus einem einzigen Objekt. Objekte fordern über Methodenaufrufe Leistungen voneinander an.

Dabei lassen sich zwei Seiten unterscheiden:

  • Eine Nachricht bezeichnet den Auftrag an ein bestimmtes Empfängerobjekt, zum Beispiel kueche.einschalten().
  • Die Methode legt innerhalb der Klasse fest, wie das Empfängerobjekt den Auftrag ausführt.

Der aufrufende Programmteil muss die öffentliche Schnittstelle kennen. Er muss nicht wissen, wie die Lampe ihren Zustand intern speichert.

Beispiel

Ein Objekt Lichtschalter kennt eine bestimmte Lampe. Beim Betätigen sendet der Schalter dieser Lampe den Auftrag einschalten().

Der Schalter entscheidet, welches Objekt den Auftrag erhält. Die Lampe entscheidet durch ihre Methode, wie sie ihn ausführt. So bleiben die Verantwortlichkeiten getrennt.

Merke

Eine gute Objektaufteilung beantwortet zwei Fragen klar: Welches Objekt ist für eine Aufgabe zuständig, und welche Schnittstelle bietet es dafür an?

Teste dich
Frage 1 von 1MittelEin Lichtschalter ruft lampe.einschalten() auf. Welche Aufgabe übernimmt die Lampe?
Lösung: Sie führt ihre Methode aus und verwaltet dabei ihren eigenen Zustand. — Der Empfänger des Aufrufs führt die zugehörige Methode aus. Dabei kann er seinen gekapselten Zustand verändern.
Vererbung und Polymorphie gezielt einsetzen

Mehrere Klassen können denselben fachlichen Vertrag erfüllen. Vererbung oder eine gemeinsame Schnittstelle sind dann sinnvoll, wenn Objekte tatsächlich austauschbar verwendet werden sollen.

Definition

Vererbung

Bei der Vererbung übernimmt eine Subklasse Bestandteile einer Superklasse und kann sie ergänzen oder geeignete Methoden überschreiben. Sie modelliert eine fachliche Ist-ein-Beziehung.

EMailMeldung und SMSMeldung können beispielsweise den gemeinsamen Vertrag Meldung mit der Methode senden() erfüllen. Jede Klasse setzt das Senden anders um.

Definition

Polymorphie

Polymorphie bedeutet hier: Verschiedene Objekttypen werden über einen gemeinsamen Typ verwendet, reagieren aber mit ihrer jeweils passenden Methodenimplementierung.

Betrachte Meldung m = new EMailMeldung(); und danach m.senden();:

  1. Die Variable besitzt den allgemeinen Typ Meldung.
  2. Sie verweist tatsächlich auf ein Objekt der Klasse EMailMeldung.
  3. Zur Laufzeit wird deshalb senden() von EMailMeldung ausgeführt.

Diese Auswahl der konkreten Methode zur Laufzeit heißt dynamisches Binden.

Beispiel

Eine Versandliste enthält E-Mail- und SMS-Meldungen. Eine Schleife behandelt jedes Element als Meldung und ruft senden() auf.

Für ein E-Mail-Objekt läuft die E-Mail-Implementierung, für ein SMS-Objekt die SMS-Implementierung. Wird später eine austauschbare PushMeldung ergänzt, muss die Schleife nicht nach konkreten Typen unterscheiden.

Die Austauschbarkeit besitzt eine wichtige Bedingung: Eine Subklasse muss den Vertrag des allgemeinen Typs einhalten. Ein Objekt darf nicht nur denselben Methodennamen tragen, sondern muss sich für die zugesagte Aufgabe passend verhalten.

Teste dich
Frage 1 von 3LeichtWovon hängt bei einem dynamisch gebundenen Methodenaufruf die ausgeführte Implementierung ab?
Lösung: Vom tatsächlichen Objekt, auf das die Referenz zur Laufzeit verweist. — Bei dynamischer Bindung entscheidet der tatsächliche Objekttyp über die überschriebene Methode.
Frage 2 von 3MittelMeldung m = new SMSMeldung(); wird von m.senden(); gefolgt. Welche Methode läuft?
Lösung: Die Implementierung von SMSMeldung. — Die Referenz zeigt auf ein SMS-Objekt. Deshalb wird dessen passende Implementierung gewählt.
Frage 3 von 3SchwerWann ist eine neue Klasse PushMeldung sicher über den Typ Meldung einsetzbar?
Lösung: Wenn sie den Vertrag von Meldung erfüllt und senden() passend implementiert. — Austauschbarkeit beruht auf einem gemeinsamen, eingehaltenen Vertrag und nicht auf Namen oder Sonderfällen im aufrufenden Code.
Wann Objektorientierung hilft und wann sie belastet

Objektorientierung kann Zuständigkeiten sichtbar machen, interne Daten schützen und neue austauschbare Typen ermöglichen. Sie ist aber kein automatisches Gütesiegel.

HilfreichProblematisch
Objekte besitzen klar getrennte Verantwortlichkeiten.Viele kleine Klassen erschweren den Überblick.
Stabile Schnittstellen verbergen veränderliche Details.Unnötige Abstraktion macht eine einfache Aufgabe kompliziert.
Austauschbare Typen ersparen Fallunterscheidungen.Tiefe Vererbungshierarchien machen Verhalten schwer nachvollziehbar.
Gemeinsame Funktionalität wird sinnvoll wiederverwendet.Vererbung wird nur zur Vermeidung weniger Codezeilen eingesetzt.
Gut zu wissen

Frage vor einer Vererbung: Ist jedes Objekt der Subklasse fachlich überall dort einsetzbar, wo ein Objekt der Superklasse erwartet wird? Wenn nicht, ist die Hierarchie wahrscheinlich unpassend.

Teste dich
Frage 1 von 1SchwerEin Programm besitzt eine tiefe Klassenhierarchie, aber die Unterklassen sind nicht zuverlässig austauschbar. Welche Folgerung ist am besten begründet?
Lösung: Die Hierarchie sollte überprüft und gegebenenfalls vereinfacht werden. — Vererbung ist sinnvoll, wenn sie eine tragfähige fachliche Beziehung und Austauschbarkeit ausdrückt. Andernfalls erhöht sie vor allem die Komplexität.
Karteikasten
Karteikasten

Überlege zuerst selbst und drehe die Karte anschließend zum Prüfen um.

Alles auf einen Blick
Mindmap
  • Objektorientierung
    • Objekt: Identität, Zustand und Verhalten
    • Klasse: gemeinsamer Aufbau für Instanzen
    • Konstruktor: gültiger Anfangszustand
    • Kapselung: kontrollierter Zugriff über Methoden
    • Kooperation: Aufträge an zuständige Objekte
    • Vererbung: fachliche Spezialisierung
    • Polymorphie: gemeinsame Verwendung austauschbarer Typen
    • Dynamisches Binden: Methodenwahl nach tatsächlichem Objekt
Abschluss-Check
Teste dich
Frage 1 von 5LeichtWas unterscheidet eine Klasse von einem Objekt?
Lösung: Die Klasse beschreibt den Typ; das Objekt ist eine konkrete Instanz mit eigener Identität. — Die Klasse legt den gemeinsamen Aufbau fest. Erst eine Instanz besitzt ihre konkrete Identität und ihre eigenen Attributwerte.
Frage 2 von 5MittelEine Referenzvariable wurde deklariert, aber es wurde kein Objekt erzeugt. Welche Aussage stimmt?
Lösung: Die Deklaration allein hat noch keine Instanz erzeugt. — Eine Referenzvariable kann auf ein Objekt verweisen. Sie ist aber nicht das Objekt und erzeugt es nicht allein durch ihre Deklaration.
Frage 3 von 5MittelWarum verändert ein Objekt seinen privaten Zustand am besten über eigene Methoden?
Lösung: So kann es Regeln für gültige Zustandsänderungen zentral einhalten. — Kapselung lässt das Objekt den Zugriff auf seinen Zustand kontrollieren und schützt seine Konsistenz.
Frage 4 von 5SchwerEine Liste vom Typ Meldung enthält eine EMailMeldung und eine SMSMeldung. Für beide wird senden() aufgerufen. Was geschieht?
Lösung: Jedes Objekt führt zur Laufzeit seine eigene passende Implementierung aus. — Gemeinsamer Typ und dynamisches Binden ermöglichen Polymorphie: gleicher Aufruf, zum tatsächlichen Objekt passende Ausführung.
Frage 5 von 5SchwerDu ergänzt eine neue Unterklasse. Welcher Test prüft die entscheidende Austauschbarkeit?
Lösung: Verwende ein Objekt der Unterklasse an einer Stelle, die den allgemeinen Typ erwartet, und prüfe den zugesagten Vertrag. — Eine geeignete Unterklasse muss sich an Stellen des allgemeinen Typs vertragsgemäß verhalten. Genau diese Verwendung sollte der Test überprüfen.

Passend dazu