Die objektorientierte Programmierung (OOP) ist heute so vertraut, dass leicht vergessen wird, dass sie aus einer konkreten historischen Entwicklung des Softwareentwurfs hervorging. Klassen, Objekte, Methoden, Vererbung, Schnittstellen und Polymorphie entstanden weder gleichzeitig noch als universelles Rezept.
Objektorientierung begann mit einer praktischen Frage: Wie lassen sich Entitäten mit Zustand, Verhalten und Beziehungen zu anderen Entitäten in einem Programm darstellen? Von Simula über Smalltalk bis zu C++, Java, Python und JavaScript änderten sich die Antworten deutlich.
Vor den Objekten
Höhere Programmiersprachen der 1950er- und 1960er-Jahre brachten Funktionen, Prozeduren, Datenstrukturen und Module. Mit wachsenden Systemen wurde es jedoch schwierig, Daten zu ändern, die von vielen verstreuten Funktionen bearbeitet wurden. Eine besonders einflussreiche Antwort entstand in der Simulation.
Simula: Akteure einer Simulation darstellen
Die norwegischen Informatiker Ole-Johan Dahl und Kristen Nygaard entwickelten in den 1960er-Jahren Simulationssprachen. Mit Simula 67 führten sie grundlegende Mechanismen ein: Klassen, Objekte, Vererbung und virtuelle Methoden. Eine Klasse beschreibt eine Kategorie, ein Objekt eine konkrete Instanz.
Klasse, Objekt und Alan Kays andere Sicht
Im klassischen Modell definiert eine Klasse gemeinsame Struktur und Verhalten; ein Objekt ist eine konkrete Instanz mit eigenem Zustand. Dieses Modell prägt C++, Java und C#, ist aber nicht universell: prototypbasierte Systeme funktionieren anders.
Ende der 1960er- und Anfang der 1970er-Jahre entwickelte Alan Kay eine andere Sicht, die unsere Biografie über Alan Kay ausführlicher beschreibt. Für ihn waren autonome Objekte, die Nachrichten austauschen, wichtiger als Klassenhierarchien.
Smalltalk: Alles wird zum Objekt
Am Xerox PARC entwickelten Alan Kay, Dan Ingalls, Adele Goldberg und weitere Forscher Smalltalk. Zahlen, Sammlungen und Klassen gehören zum Objektmodell, Kommunikation geschieht über Nachrichten. Die interaktive Entwicklungsumgebung beeinflusste Personal Computing und spätere IDEs stark.
Zustand, Identität und Verhalten
Ein Objekt verbindet typischerweise Zustand, Verhalten und Identität. Zwei Bankkonten können denselben Kontostand besitzen und dennoch verschiedene Konten sein. In manchen Domänen ist diese Identität wichtig; in anderen sind unveränderliche Werte und Funktionen geeigneter. OOP ist daher ein Werkzeug, keine universelle Pflicht.
Kapselung und Abstraktion
Kapselung verbindet Zustand mit den Operationen, die ihn verändern, und begrenzt den Zugriff auf interne Details. Ihr Ziel ist, Abhängigkeiten zu reduzieren und Änderungen hinter einer stabilen öffentlichen Schnittstelle zu ermöglichen.
Abstraktion zeigt, was eine Komponente leisten kann, ohne ihre interne Umsetzung offenzulegen. OOP hat Abstraktion nicht erfunden, aber eine Form popularisiert, die Zustand und Verhalten in Entitäten bündelt.
Vererbung und Polymorphie
Vererbung erlaubt einer Klasse, Eigenschaften einer anderen zu übernehmen und zu spezialisieren. Sie kann Code wiederverwenden, erzeugt aber auch enge Kopplung und starre Hierarchien. Deshalb gilt sie heute als eines von mehreren Werkzeugen.
Polymorphie ermöglicht, unterschiedliche Objekte über ein gemeinsames Verhalten zu verwenden. Je nach Sprache geschieht dies durch Vererbung, Interfaces, Protokolle, Duck Typing oder Generics. Entscheidend ist: gegen einen Vertrag statt gegen eine konkrete Implementierung programmieren.
C++ und Objective-C
Ab 1979 verband Bjarne Stroustrup Ideen aus Simula mit der Effizienz von C. Daraus entstand C++, das OOP in der Industrie verbreitete, ohne andere Paradigmen aufzugeben.
Objective-C, von Brad Cox und Tom Love entwickelt, ging einen anderen Weg: eine dynamische, von Smalltalk inspirierte Objektschicht über C. Die Sprache wurde bei NeXT und später Apple wichtig.
Die 1990er: OOP wird Mainstream
In den 1990er-Jahren wurde OOP zum dominierenden Modell. Grafische Oberflächen eigneten sich gut für Objekte, während Design Patterns und UML professionelle Entwurfsmethoden prägten. Objektorientierung wurde zeitweise als allgemeine Lösung für nahezu jedes System dargestellt – mit dauerhaften Fortschritten, aber auch Übertreibungen.
Java, C# und industrielle Verbreitung
Java erschien 1995 mit Klassen, Garbage Collection und Interfaces. Unsere Geschichte von Java und die Biografie von James Gosling behandeln diese Entwicklung ausführlicher.
C# übernahm später vertraute Ideen in .NET und entwickelte sich mit Generics, Lambdas, LINQ, Records und Pattern Matching weiter. Selbst stark objektorientiert geprägte Sprachen wurden damit multiparadigmatisch.
Python und JavaScript
Python unterstützt Klassen und Polymorphie, zwingt aber nicht jedes Programm in Klassen. Duck Typing richtet den Blick häufig auf vorhandenes Verhalten statt auf eine feste Hierarchie.
JavaScript, geschaffen von Brendan Eich, basiert historisch auf Prototypen. Die moderne class-Syntax baut weiterhin auf diesem Mechanismus auf.
Komposition statt Vererbung
Mit zunehmender Erfahrung verbreitete sich die Empfehlung Komposition vor Vererbung. Komposition reduziert häufig Kopplung und erleichtert den Austausch von Komponenten. Sie macht Vererbung nicht überflüssig, verhindert aber, dass jede Wiederverwendung in einen Klassenbaum gezwungen wird.
Interfaces, SOLID und Entwurfsprinzipien
Interfaces trennen den Bedarf einer Komponente von der konkreten Implementierung. Diese Idee trägt Dependency Injection und mehrere SOLID-Prinzipien.
SOLID sind Heuristiken, keine Gesetze. Mechanische Anwendung kann unnötig viele Klassen, Interfaces und Schichten erzeugen. Eine Abstraktion lohnt sich, wenn sie eine echte Grenze von Verantwortung oder Veränderung darstellt.
Patterns, UML und Entwurfswerkzeuge
1994 popularisierte Design Patterns Muster wie Factory, Observer, Strategy, Decorator und Adapter. Ihr größter Wert liegt in einer gemeinsamen Sprache, nicht in Rezepten.
UML vereinheitlichte verschiedene Modellierungsmethoden. Klassendiagramme wurden eng mit OOP verbunden. Patterns und Diagramme helfen beim Denken, ersetzen aber keinen zum Problem passenden Entwurf.
Tests und Grenzen von Abstraktionen
Kapselung, Interfaces und Dependency Injection können Tests erleichtern, indem Abhängigkeiten durch Fakes, Stubs oder Mocks ersetzt werden. Eine Architektur nur für Mocking zu zerteilen kann jedoch das Gegenteil bewirken. Mehr Abstraktion bedeutet nicht automatisch besseren Entwurf.
Kritik und andere Paradigmen
Mit der Dominanz von OOP wurden Grenzen sichtbar: tiefe fragile Hierarchien, geteilter veränderlicher Zustand, stark vernetzte Objekte und überladene „Enterprise“-Architekturen.
Funktionale Programmierung betonte erneut reine Funktionen und unveränderliche Daten. Moderne Versionen von Java, C#, C++, Python und JavaScript kombinieren heute mehrere Ansätze. „Objektorientiert gegen funktional“ ist deshalb immer weniger eine sinnvolle Gegenüberstellung.
GUIs, Spiele und verteilte Systeme
Grafische Oberflächen und Spiele eignen sich weiterhin für Entitäten mit Zustand und Identität. Moderne Engines nutzen jedoch auch Entity Component Systems (ECS), die Komposition und getrennte Daten gegenüber tiefen Vererbungshierarchien bevorzugen.
In verteilten Systemen organisiert das Aktorenmodell Berechnung um Entitäten, die Nachrichten empfangen und eigenen Zustand halten. Ein Aktor ist kein Java- oder C++-Objekt, erinnert aber an Alan Kays Betonung von Nachrichten und Isolation.
Was bleibt von OOP?
Nach Jahrzehnten bleiben Kapselung, Polymorphie, Komposition sowie Identität und Zustand besonders dauerhaft. Die Vorstellung, gute Architektur müsse aus großen Klassenhierarchien bestehen, hat dagegen an Einfluss verloren.
Smalltalk, C++, Java, Python und JavaScript unterscheiden sich so stark, dass OOP besser als Familie von Programmiermodellen verstanden wird als als starre Checkliste.
Warum OOP weiterhin lernen?
Sehr viel bestehende Software und zahlreiche Frameworks beruhen auf Klassen und Objekten. Kapselung, Verantwortlichkeiten, Verträge und Polymorphie bleiben nützlich. Reife bedeutet weniger, „überall Objekte“ einzusetzen, als die passende Darstellung für ein Problem zu wählen.
Das Wichtigste
OOP entstand schrittweise mit Simula, wurde durch Smalltalk und Alan Kay neu interpretiert, durch C++ industriell verbreitet und mit Java zum Mainstream. Python und JavaScript zeigen dynamischere, multiparadigmatische Modelle.
Die Erfahrung korrigierte Übertreibungen: Vererbung ist nicht mehr die natürliche Antwort auf jede Wiederverwendung; Komposition, Interfaces, Unveränderlichkeit und Funktionen gewannen an Bedeutung.
Die dauerhafteste Lehre lautet nicht „alles muss eine Klasse sein“, sondern: zusammengehörige Verantwortlichkeiten bündeln, unnötige Details verbergen und Komponenten zusammenarbeiten lassen, ohne sie übermäßig an interne Implementierungen zu koppeln.
Häufig gestellte Fragen
Was ist objektorientierte Programmierung?
Eine Familie von Ansätzen, die Software um Objekte mit Zustand, Identität und Verhalten organisiert, die über Methoden, Nachrichten oder Interfaces zusammenarbeiten.
Wer hat OOP erfunden?
Es gibt keinen einzelnen Erfinder. Dahl und Nygaard führten grundlegende Mechanismen in Simula ein; Alan Kay und das Smalltalk-Team entwickelten später eine nachrichtenorientierte Sicht.
Was ist der Unterschied zwischen Klasse und Objekt?
Eine Klasse beschreibt gemeinsame Struktur und Verhalten; ein Objekt ist eine konkrete Instanz mit eigenem Zustand. Prototypbasierte Systeme funktionieren anders.
Welche Prinzipien sind wichtig?
Kapselung, Abstraktion, Vererbung und Polymorphie werden häufig genannt; moderne Praxis betont ebenso Komposition, Interfaces und Abhängigkeitsmanagement.
Ist Vererbung Pflicht?
Nein. Viele objektorientierte Systeme bevorzugen Komposition oder Interfaces.
Ist JavaScript objektorientiert?
Ja, historisch jedoch prototypbasiert. Auch die moderne class-Syntax nutzt darunter Prototypen.
Ist Python objektorientiert?
Ja, aber Python ist multiparadigmatisch und zwingt nicht alles in Klassen.
Ist OOP überholt?
Nein. Sie wird weiterhin breit eingesetzt, gilt aber nicht mehr als universelle Lösung.
Komposition oder Vererbung?
Oft erzeugt Komposition flexiblere und weniger gekoppelte Komponenten. Vererbung bleibt bei echter Spezialisierung sinnvoll.
Warum ist OOP weiterhin wichtig?
Weil Kapselung, Verantwortlichkeiten, Verträge und Polymorphie auch in multiparadigmatischen Architekturen helfen, Komplexität zu beherrschen.