Echoes from the Machine
Abstract
Objektorientierte Programmierung — was sie versprach, was sie lieferte, was sie zerbrach und was wir stillschweigend trotzdem behielten. Eine Abrechnung aus der Praxis nach 25+ Jahren im Produktivbetrieb.
Objektorientierte Programmierung — was sie versprach, was sie lieferte, was sie zerbrach und was wir stillschweigend trotzdem behielten
Eine Vorbemerkung
Dies ist kein Literaturüberblick. Es ist kein ausgefeiltes akademisches Papier. Es ist die Abrechnung eines Praktikers — geschrieben von jemandem, der in den späten 1990ern und frühen 2000ern tief in Projekten steckte, die rückblickend geradezu besessen von objektorientierter Programmierung waren. Nicht immer auf die richtige Weise.
Der Autor arbeitete in dieser Zeit an Enterprise-Dokumentenmanagement- und OCR-Systemen für EU-Institutionen, an Groupware- und Kommunikationsplattformen, an früher E-Commerce-Infrastruktur — alles in Java und C++ gebaut, alles mit der Überzeugung architekturiert, dass OOP die korrekte und vollständige Antwort auf Softwarekomplexität sei. Die Bücher waren gelesen. Booch stand im Regal. Das Gang-of-Four-Buch war mit Eselsohren versehen. Die Teams waren kompetent. Und dennoch wuchsen die Systeme auf Weisen, die ihre Architekten nicht vollständig kontrollierten — aus Gründen, die erst Jahre später klar wurden.
Was folgt, ist ein Versuch, diese Gründe ehrlich zu benennen — neben echtem Respekt für das, was das Paradigma richtig gemacht hat — und die Analyse in eine Gegenwart zu bringen, in der Microservices, funktionale Muster, verteilte Systeme und neue Werkzeuge das Terrain kollektiv verändert haben. Die Referenzen sind echt. Die Meinungen sind die des Autors. Der Ton ist bewusst informell: Dies ist für die Community gedacht, nicht für den Ausschuss.
— P.M., Nürnberg, 2026
Inhalt
- Genesis — Herkunft und Bedeutung
- Die vier Säulen — und was die Lehrbücher verschweigen
- SOLID, Patterns und das Architektur-Wettrüsten
- Stärken, Pathologien und die ehrlichen Kompromisse
- OOP in der heutigen Landschaft — was überlebte und was nicht
- Praktische Umsetzung — was wirklich funktioniert
- Schluss — wo wir tatsächlich stehen
I. Genesis — Herkunft und Bedeutung
Bevor wir die Kathedrale kritisieren können, sollten wir verstehen, warum sie gebaut wurde und was sie ersetzte.
Objektorientierte Programmierung entstand nicht im Vakuum. Sie entstand aus einer Krise. Mitte der 1960er Jahre war das dominante Paradigma prozeduraler Code — große, flache, weitgehend undifferenzierte Programme, die brillant funktionierten, wenn sie klein waren, und zu existenziellen Albträumen wurden, wenn sie wuchsen. Das Problem hatte einen Namen: die Softwarekrise. [1]
Projekte überzogen. Systeme versagten. Dijkstra schrieb seinen berühmten Brief, der GOTO verurteilte, und löste ein Jahrzehnt des Nachdenkens in der Informatik aus.
Simula 67, entwickelt von Ole-Johan Dahl und Kristen Nygaard am Norwegischen Rechenzentrum, führte die Konzepte ein, die grundlegend werden sollten: Klassen, Objekte, Vererbung. Das Ziel war Simulation — die Modellierung realer Entitäten im Code. Alan Kay übernahm die Fackel bei Xerox PARC, baute Smalltalk und prägte den Begriff „objektorientiert." Seine Vision war fast biologisch: autonome Objekte, die über Nachrichten kommunizieren, wie Zellen in einem Organismus. [2]
„Die große Idee ist ‚Messaging’ — das ist der Kern von Smalltalk/Squeak. Der Schlüssel zur Entwicklung großartiger und erweiterbarer Systeme liegt viel mehr darin, wie die Module kommunizieren, als darin, welche internen Eigenschaften und Verhaltensweisen sie haben sollten."
— Alan Kay, E-Mail an die Squeak-dev-Liste, 1998
Dies ist wichtiger Kontext: OOP, wie Kay es konzipierte, handelte nicht von Klassenhierarchien. Es handelte von Kapselung und Message-Passing. Was die Industrie anschließend damit machte — das klassenreiche, vererbungsorientierte, pattern-besessene Ökosystem, das die Software von Mitte der 1980er bis Mitte der 2010er dominierte — ist nach Kays eigenen Worten nicht das, was er meinte.
II. Die vier Säulen — und was die Lehrbücher verschweigen
Kapselung, Abstraktion, Vererbung, Polymorphismus. Diese Liste haben Sie tausendmal gehört. Hier ist, was die Folie weglässt.
Kapselung — die gute
Kapselung ist die am wenigsten kontroverse der vier und die universell anwendbarste. Daten und das Verhalten, das auf diesen Daten operiert, bündeln; die Interna verbergen. Diese Idee überlebt die Übersetzung in fast jedes Paradigma. Ein Python-Modul ist eine Form der Kapselung. Ein Rust-Struct mit privaten Feldern ist Kapselung. Eine gut gestaltete REST-API ist Kapselung. Wenn Ingenieure irgendwann von klassenbasiertem OOP zu anderen Ansätzen wechseln, behalten sie typischerweise die Kapselung. Das sagt etwas aus.
Die Falle ist die Verwechslung von Kapselung mit bloßem Daten-Verstecken. Private Felder, die vollständig durch öffentliche Getter und Setter exponiert werden — das JavaBean-Muster — erreichen nichts außer Ausführlichkeit. Echte Kapselung bedeutet, dass die Schnittstelle des Objekts eine kohärente Geschichte darüber erzählt, was es tut — kein undichtes Fenster in seinen internen Zustand.
Antipattern — Das Anämische Domänenmodell
Martin Fowler nannte dieses Antipattern 2003: Domänenobjekte, die Datenbehälter ohne sinnvolles Verhalten sind, während die Geschäftslogik in Service-Klassen lebt, die prozedural auf ihnen operieren. Das Ergebnis ist nominell OO-Code, der funktional prozedural ist — das Schlechteste aus beiden Welten.
Abstraktion — die missbrauchte
Abstraktion ist die Kunst, die richtige Verallgemeinerungsebene zu identifizieren. In der Praxis abstrahieren Teams entweder zu spät — konkreter Spaghetti-Code, der nicht erweitert werden kann — oder viel zu früh, indem sie Interfaces für Probleme bauen, die noch nicht existieren und möglicherweise nie existieren werden. Der Satz „Wir müssen vielleicht später die Datenbank austauschen" hat tausend unnötige Abstraktionsebenen gestartet, von denen die meisten nie ausgetauscht wurden.
Vererbung — die gefährliche
Vererbung ist, wo OOP den meisten intellektuellen Schaden angerichtet hat. Die Intuition ist verführerisch: Wenn ein Hund ein Tier ist, warum nicht Tier erweitern und sein Verhalten erben? Weil reale Taxonomien komplexer sind als Compile-Zeit-Hierarchien, und was als sauberer Vererbungsbaum beginnt, wird typischerweise zu einem fragilen Netzwerk von Annahmen, das jede zukünftige Änderung bestraft.
„Komposition der Vererbung vorziehen."
— Gamma, Helm, Johnson, Vlissides — Design Patterns, 1994 [4]
Polymorphismus — der mächtige
Polymorphismus — verschiedene Typen einheitlich über eine gemeinsame Schnittstelle behandeln — ist wohl OOPs dauerhaftester Beitrag. Er ist der Motor hinter Dependency Injection, dem Strategy-Pattern, Mock-basiertem Testen und Plugin-Architekturen. Er ist auch in verschiedenen Formen in Sprachen verfügbar, die nicht objektorientiert sind. Go-Interfaces, Haskells Typklassen, Rusts Traits bieten alle Polymorphismus ohne Klassenhierarchien.
III. SOLID, Patterns und das Architektur-Wettrüsten
Die 1990er und 2000er Jahre sahen OOP kodifiziert, formalisiert und — in erheblichem Maß — gegen die Probleme selbst eingesetzt, die es lösen sollte.
Robert Martins SOLID-Prinzipien [5] sind ein echter Versuch, hart erarbeitete Lektionen über wartbares OO-Design zu destillieren. Jedes Prinzip adressiert einen echten Fehlermodus. Jedes wurde auch mechanisch angewendet, auf Weisen, die mehr Abstraktion, mehr Dateien, mehr Interfaces und weniger Klarheit produzieren.
Das Gang-of-Four-Buch Design Patterns [4] ist ein wirklich wichtiges Buch, das wirklich missbraucht wurde. Patterns sind Vokabular zur Kommunikation von Designentscheidungen unter erfahrenen Ingenieuren. Sie sind keine Vorschriften. Wenn ein Team fragt „Welches Pattern passt hier?" bevor es das Problem verstanden hat, betreibt es Cargo-Culting — die äußere Form guter Architektur zu bauen ohne die Substanz.
IV. Stärken, Pathologien und die ehrlichen Kompromisse
Echte Stärken:
- Kapselung reduziert die Angriffsfläche für Bugs und macht Systeme lokal verständlich
- Polymorphismus ermöglicht Testbarkeit durch Dependency Injection und Interface-Mocking
- Domänenmodellierung richtet die Codestruktur an Geschäftskonzepten aus — wenn gut gemacht
- Reifes Ökosystem: Werkzeuge, IDEs, Profiler, statische Analyse um OO-Annahmen herum gebaut
- Klassenebene-Ownership gibt Teams klare Verantwortungseinheiten im großen Maßstab
Echte Pathologien:
- Tiefe Vererbungshierarchien sind fragil — sie bestrafen jede Anforderungsänderung
- Gemeinsamer veränderbarer Objektzustand ist eine Nebenläufigkeitsgefahr in Multithreading-Systemen
- Überabstraktion erhöht die kognitive Last ohne die tatsächliche Komplexität zu reduzieren
- OOP fördert die Modellierung der Welt, nicht der Berechnung — schlecht geeignet für Pipelines
V. OOP in der heutigen Landschaft
Die Paradigmenkriege sind weitgehend vorbei. Die Überlebenden sind pragmatisch, und die interessanteste Arbeit findet an den Grenzen zwischen Ansätzen statt.
Die bedeutendste Verschiebung des letzten Jahrzehnts war die stille Mainstreaming funktionaler Ideen in nominell objektorientierten Sprachen. Java hat jetzt Lambdas, Streams und Records. C# hat LINQ, Pattern Matching und unveränderliche Werttypen. Die Industrie hat kollektiv entschieden, dass reines OOP und rein funktionale Programmierung beide zu starr sind, und die gute Arbeit an der Schnittstelle stattfindet.
In Finanzsystemen — wo dieser Autor beträchtliche Zeit verbracht hat — hat der funktionale Ansatz besondere Resonanz. Eine Pricing-Engine, die Berechnung als reine Funktion von Marktdaten zu fairen Werten behandelt, ist prüfbar, reproduzierbar und parallelisierbar. Peyton Jones et al. demonstrierten dies elegant in ihrer Arbeit über die Komposition von Finanzkontrakten [7] — ein Paper aus dem Jahr 2000, das praktisch relevanter bleibt als das meiste, was in demselben Jahrzehnt über OOP veröffentlicht wurde.
VI. Praktische Umsetzung — was wirklich funktioniert
Prinzipien sind gut. Hier ist, was die Narben nahelegen.
Mit Daten beginnen, nicht mit Objekten
Bevor Sie nach einer Klasse greifen, verstehen Sie die Daten — ihre Form, ihre Lebensdauer, wem sie gehören, wie sie fließen. Datenmodelle neigen dazu, stabil zu sein; Verhalten neigt dazu, sich zu ändern.
|
|
Komposition statt Vererbung
Das Komposition-über-Vererbung-Prinzip ist eine Überlebensstrategie, keine stilistische Präferenz. Jede Ebene der Vererbung, die Sie hinzufügen, ist eine Kopplung, die Sie irgendwann bereuen werden.
Side Effects als Infrastruktur behandeln
Der wartbarste Code — OO oder anderweitig — schiebt Side Effects an die Ränder: IO, Datenbankschreibvorgänge, Netzwerkaufrufe. Die Kerndomänenlogik operiert so nah wie möglich an reinen Funktionen. In regulierten Umgebungen, wo jede Berechnung reproduzierbar sein muss, ist dies keine Luxus. Es ist eine Anforderung.
VII. Schluss — wo wir tatsächlich stehen
OOP ist nicht gescheitert. Es gelang gut genug, dass seine Exzesse zum Problem wurden. Die Industrie verbrachte zwanzig Jahre damit, darauf aufzubauen, und die letzten zehn Jahre damit, es stillschweigend zu korrigieren. So funktioniert reife Ingenieurspraxis.
Die zeitgenössische Softwarearchitekturlandschaft ist in der Praxis post-paradigmatisch. Moderne Systeme sind in Schichten gebaut: Infrastruktur als Code verwaltet, Services über klar definierte Kontrakte kommunizierend, Domänenlogik in welcher Kombination aus OO und funktionalen Mustern auch immer zum Problem passt. Niemand entwirft heute ein Greenfield-System und fragt „Welches Paradigma verwenden wir?" Sie fragen, wie die Daten aussehen, wie sie fließen, was die Fehlermodi sind.
OOPs echte Beiträge bestehen fort. Kapselung ist überall — in Modulsystemen, in Service-Grenzen, im API-Design. Polymorphismus ist überall — in Interface-basierter Dependency Injection, in protokollbasierten Typsystemen.
Die Architekturentscheidungen, die im Laufe der Zeit standhalten, teilen einige Eigenschaften: Sie halten das Datenmodell stabil und lassen das Verhalten variieren. Sie schieben Side Effects an die Ränder. Sie bevorzugen explizite Kontrakte gegenüber impliziten Annahmen. Sie behandeln Komplexität als Kosten, nicht als Feature.
„Der Zweck der Abstraktion ist nicht, vage zu sein, sondern eine neue semantische Ebene zu schaffen, auf der man absolut präzise sein kann."
— Edsger W. Dijkstra, „The Humble Programmer", ACM Turing Lecture, 1972 [8]
Das ist immer noch das Spiel. Alles andere ist Implementierungsdetail.
Referenzen
- Naur, P. & Randell, B. (Hrsg.) Software Engineering: Report of a Conference. NATO Science Committee, Garmisch, Deutschland, 1968.
- Kay, A. C. The Early History of Smalltalk. ACM SIGPLAN Notices, 28(3), 1993.
- Booch, G. Object-Oriented Analysis and Design with Applications, 3. Aufl. Addison-Wesley, 2007.
- Gamma, E., Helm, R., Johnson, R., Vlissides, J. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1994.
- Martin, R. C. Agile Software Development, Principles, Patterns, and Practices. Prentice Hall, 2002.
- Meyer, B. Object-Oriented Software Construction, 2. Aufl. Prentice Hall, 1997.
- Peyton Jones, S. et al. Composing contracts: an adventure in financial engineering. ACM SIGPLAN Notices, 35(9), 2000.
- Dijkstra, E. W. The Humble Programmer. Communications of the ACM, 15(10), 1972.
Echoes from the Machine · Informeller technischer Essay · Software Architecture Series Argumente mit der Absicht vorgetragen, Denken zu provozieren, nicht Konsens. Frei zu verteilen.