Eine Einführung in die OOP-Grundbegriffe

Paradigmenwechsel erfordert die Kenntnis der neuen Terminologie

06.11.1992

Objektorientierte Programmierung ist ein aktuelles Lieblingsthema bei Veröffentlichungen und Tagungen. Sie wird nicht nur als Ausweg aus der Softwarekrise, sondern sogar als Prachtstraße zu Software-Entwurf und -Realisierung bezeichnet. Doch ihren Charakter verbirgt sie - nicht nur für DV-Laien - zunächst hinter dem Wall eines neuartigen Vokabulars.

Was hat die Produktion von Software in der heutigen Zeit mit der von Handfeuerwaffen im 18. Jahrhundert gemeinsam? Einen uneffektiven Herstellungsprozeß, der sich auch auf den späteren Lebenszyklus des Produkts negativ auswirkt. Was das ältere Problem betrifft, hatte Eli Whitney im Jahre 1798 die bahnbrechende Idee.

Gewehre wurden fortan nicht mehr von einem Büchsenmacher als Ganzes gefertigt, sondern Spezialisten übernahmen die Fabrikation standardisierter Einzelteile, die sich dann zum fertigen Produkt zusammensetzen ließen.

Diese Umstellung erleichterte nicht nur Reparaturen und Wartungsarbeiten, sondern ermöglichte auch einen Austausch entsprechender Komponenten zwischen verschiedenen Waffen.

Wiederverwertung soll Kosten senken

Ein derartiger Übergang zu industriellen Fertigungsmethoden steht nach Auffassung vieler Fachleute auch für die Software-Entwicklung an. Ziel ist die Senkung von Entwicklungs- und Wartungskosten mit Hilfe wiederverwendbarer Softwarekomponenten, die außerdem sehr zuverlässig und leicht erweiterbar sein sollen. Wie die Softwarekrise belegt, ist dies mit der heute üblichen Methode der strukturierten Programmierung nicht erreichbar.

Die auf diese Weise erstellten Softwarepakete sind in der Regel komplizierte Einzelstücke, deren Bestandteile in vielfältigen Wechselbeziehungen zueinander stehen. Änderungen und Erweiterungen in einem solchen System sind in ihren Auswirkungen kaum überschaubar, und die durch sie verursachten Kosten übersteigen nicht selten die einer Neuentwicklung. Auch die Wiederverwendung einzelner Komponenten eines solchen Systems ist eher die Ausnahme.

Besonders in den letzten beiden Jahren haben Fachleute als Ausweg aus diesem Dilemma die objektorientierte Programmierung propagiert. Mit ihr verbunden ist eine völlig neue Terminologie mit Begriffen wie Klasse, Vererbung, Polymorphismus etc., die zunächst einmal den Zugang erschweren. In Ermangelung eines wissenschaftlichen Fundaments, wie es für relationale Datenbanken besteht, haben diese Begriffe in den bestehenden Programmiersprachen recht unterschiedliche Ausprägungen erfahren. Im folgenden möchte ich programmiersprachenunabhängig die grundlegenden Ideen und Begriffe der objektorientierten Programmierung vorstellen und untersuchen, inwiefern sie dazu geeignet sind, zur Lösung der oben angesprochenen Probleme beizutragen.

Es ist heutzutage unumstritten, daß die Erstellung komplexer Systeme nur durch Modularisierung zu bewältigen ist. Doch welche Kriterien sind dazu heranzuziehen?

Die strukturierte Programmierung beruht auf einer funktionalen Zergliederung des Gesamtsystems in Teilsysteme bis hinunter zur Ebene individueller Unterprogramme.

Die objektorientierte Programmierung bricht radikal mit dieser Sichtweise zugunsten der Haltung, daß die Modularisierung durch die zu bearbeitenden Dinge der realen Welt auf, natürliche Art vorgegeben ist. Ein "Objekt" ist ein Softwarebaustein, der aus Daten sowie aus Prozeduren zu ihrer Bearbeitung besteht. Die Daten eines Objekts, die sich natürlich während des Ablaufs eines Programms ändern können, werden wie in der traditionellen Programmierung "Variable" genannt. "Prozeduren" heißen in der objektorientierten Terminologie die Methoden des Objekts.

Zur Erläuterung, der neuen Begriffe mag ein Beispiel dienen. Ein Rechteck in einem Zeichenprogramm ist charakterisiert durch die Bildschirmkoordinaten seiner Eckpunkte. Die Variablen des Objekts Rechteck sind hier die Bildschirmkoordinaten. Methoden sind die Operationen, die sich auf das Rechteck beziehen können also zum Beispiel "Zeichen", "Diagonale anlegen", "Verschiebe", "Zoomen" und "Fläche berechnen".

Ein wesentliches Charakteristikum eines Objektes besteht darin, daß auf die Werte seiner Variablen ausschließlich mittels der zum Objekt gehörenden Methoden zugegriffen werden kann. Dieser Mechanismus den Datenkapselung schützt einerseits die Daten eines Objektes vor ungewollten beziehungsweise unkontrollierten Veränderungen - man denke an gewöhnliche Variablen in konventionellen Programmen -, andererseits erlaubt er die Verwendung des Objektes, ohne daß man Details der Implementierung seiner Methoden kennt (Geheimnisprinzip).

Üblicherweise wird ein Programmsystem viele Objekte des gleichen Typs bearbeiten, und es wäre nicht rationell, dieselben Methoden in jedem einzelnen Objekt separat zu speichern.

Eine elegante und wirkungsvolle Methode zur Vermeidung dieses Aufwands bietet das Konzept der "Klasse". Klassen stellen Schablonen zur Definition der Methoden und Variablen gleichartiger Objekte dar. Die Objekte selber sind die "Instanzen" einer Klasse und enthalten lediglich die konkreten Werte ihrer Variablen. Im Rahmen unseres Beispiels bedeutet dies, nun eine Klasse "Rechteck" einzuführen, in der festgelegt ist, daß zu einem Rechteck seine Bildschirmkoordinaten und die oben genannten Methoden "Zeichnen" etc. gehören. Ein konkretes Rechteck R1 ist dann eine Instanz der Klasse "Rechteck", ein Objekt.

Ein zentraler Mechanismus der objektorientierten Programmierung ist die "Vererbung" der Eigenschaften, einer Klasse auf Unterklassen. Eine solche Unterklasse entsteht durch Spezialisierung einer bestehenden Klasse und besitzt automatisch alle Variablen und Methoden ihrer Oberklasse. Darüber hinaus kann sie jedoch zusätzlich eigene Methoden und Variablen einführen sowie ererbte überschreiben. Dieser Vererbungsmechanismus läßt sich theoretisch über beliebig viele Stufen hinweg fortsetzen, wobei auf jeder neuen Stufe der Klassenhierarchie lediglich die Erweiterungen und Änderungen zur darüberliegenden Stufe zu implementieren sind.

Generische Klasse an der Spitze der Hierarchie

An der Spitze der Hierarchie steht häufig eine "generische Klasse", die aus organisatorischen Gründen oder zur Definition der Variablen und Methoden ihrer Unterklassen eingeführt wird. Generische Klassen haben keine Instanzen.

Zur Fortführung unseres Beispiels ist es sinnvoll, eine generische Klasse "Polygon" einzuführen, in der wieder die Methoden "Zeichnen", "Verschieben", "Zoomen" und "Fläche" definiert sind. Die bereits bekannte Klasse "Rechteck" braucht diese dann nicht mehr zu implementieren, sondern erbt sie aus "Polygon". Da die Formel zur Flächenberechnung für Rechtecke sehr einfach ist, wird diese in "Rechteck" neu implementiert und damit die Methode gleichen Namens aus "Polygon" überschrieben. Nur die Methode "Diagonale", die sich auf eine Eigenart von Rechtecken bezieht, gibt es in "Polygon" nicht, sie stellt also eine Erweiterung gegenüber der Oberklasse dar.

Der Aufbau solcher Klassenhierarchien entspricht genau der Art und Weise, in der wir im täglichen Leben unsere Umwelt analysieren und unser Wissen abspeichern. Die objektorientierte Programmierung bietet einen natürlichen Weg zum Aufbau komplexen, aber dennoch gut verständlicher Softwaresysteme, bestehend aus weitgehend eigenständigen Objekten, die lediglich über die Methoden ihrer Klasse miteinander in Wechselwirkung treten.

Damit wird nun die Frage aufgeworfen, wie ein derartiges Softwaresystem denn funktioniert, wie Aktivitäten initiiert werden und ablaufen. Der Schlüssel hierzu sind Botschaften, die zwischen den Objekten hin- und hergehen. Eine Botschaft besteht aus dem Namen des adressierten Objekts, dem Aufruf einer Methode dieses Objekts sowie den gegebenenfalls zur Ausführung der Methode benötigten Informationen in Form einer Parameterliste. Ein Objekt, das eine derartige Botschaft erhält, durchsucht, beginnend bei seiner eigenen Klasse, seine Klassenhierarchie von unten nach oben nach der Implementierung der angesprochenen Methode und führt die zuerst gefundene Version aus. So geschieht auch die Suche nach der Definition von Variablen. In der Regel erfolgt dann nach Ausführung der gewünschten Aktion eine Bestätigung an den Sender, beispielsweise in Form eines Rückgabewertes.

Empfängt in unserem Beispiel ein Rechteck R1 die Botschaft, um den Faktor 1,5 zu zoomen, so findet es die Implementierung dieser Methode in seiner Oberklasse "Polygon". Die Methode zur Berechnung der Fläche kann es seiner eigenen Klasse entnehmen. Ferner gehört zum Zoomen die Übergabe des Vergrößerungsfaktors als Parameter, während die Methode zur Berechnung der Fläche ohne Parameter auskommt. Führt man als weitere Unterklasse von "Polygon" eine Klasse "Dreieck" ein, die wie "Rechteck" eine eigene Methode zur Flächenberechnung hat, so läßt sich diese ebenfalls mit "Fläche" bezeichnen, da jedes Objekt Botschaften gleichen Namens gemäß seiner eigenen Implementierung der Methode verarbeitet. Diese Fähigkeit, eine Botschaft durch verschiedene Objekte unterschiedlich zu interpretieren, heißt Polymorphismus.

In der Praxis gibt es noch Schwierigkeiten...

Die objektorientierte Programmierung kann zur angestrebten industriellen Fertigung von Software zweifellos beitragen. Das Modell der Klassen in Verbindung mit der Vererbung bietet eine gute Basis zur Erstellung wiederverwendbarer Softwaremodule. Denn einerseits bildet eine Klasse eine geschlossene Einheit, die in Klassenbibliotheken zur Verfügung zu stellen ist, um die Integration in beliebige Systeme zu ermöglichen. Hierbei kommt der Datenkapselung und dem Polymorphismus große Bedeutung zu. Andererseits ist es durch Vererbung leicht möglich, bestehende Klassen als Oberklassen zu verwenden und daraus neue mit speziellen Eigenschaften abzuleiten.

Ein entscheidender Vorteil dieser Konzeption ist dabei die Möglichkeit, auf bereits erprobte Softwaremodule aufzubauen und damit Entwicklungs- und Testzeiten erheblich zu reduzieren. Auch für die Wartung und Pflege bereits bestehender Systeme bieten sich günstige Perspektiven.

Änderungen in der Implementierung der Methoden einer Klasse haben keine Auswirkung nach außen, solange die Parameterliste zum Aufruf der Methode unverändert bleibt. Da Änderungen sich automatisch durch die Klassenhierarchie nach unten fortpflanzen, ist kein großer Implementierungsaufwand erforderlich. Selbst umfangreiche Änderungen bleiben überschaubar, da die Vernetzung objektorientierter Programmsysteme bei weitem nicht so vielschichtig ist wie in herkömmlichen Systemen.

... aber die Zukunft ist vielversprechend

Theoretisch präsentiert sich die objektorientierte Zukunft sehr vielversprechend. Der Weg zum endgültigen Durchbruch in der Praxis wird jedoch von vielerlei Schwierigkeiten begleitet sein, angefangen beim Kampf um die Akzeptanz der neuen Denkweise über die Entwicklung von Standards und wirklich wiederverwendbaren, umgebungsunabhängigen Klassenbibliotheken bis hin zur Bereitstellung geeigneter Entwicklungstechniken und -umgebungen. Doch auch die Idee von Eli Whitney mußte viele Widerstände überwinden, um das zu werden, was sie heute ist: eine Selbstverständlichkeit!

OOP-Ansätze gibt es seit 25 Jahren

Die grundlegenden Konzepte der objektorientierten Programmierung sind keineswegs neu. Die erste Programmiersprache, die den Begriff der Klasse verwendete, war Simula 67, eine objektorientierte Erweiterung von Algol, die Mitte der 60er Jahre im norwegischen Computerzentrum in Oslo entstand.

Ebenso wie Simula keine rein objektorientierte Sprache ist, sind auch die meisten Sprachen, die heute als objektorientiert bekannt sind, Hybridsprachen, also Erweiterungen herkömmlicher Sprachen um verschiedene Features der Objektorientierung. Als Basissprachen treten neben Algol (mit der Erweiterung Simula) beispielsweise auf: Pascal (Turbo-Pascal 6.0, Quick Pascal), C (C++, Objective C), Lisp (CLOS, Flavours, XLisp). In der Regel ist dabei die Basissprache um ein Klassenkonstrukt mit Vererbungsmöglichkeiten erweitert. Demgegenüber, gibt es die puren objektorientierten Sprachen wie Smalltalk und Eiffel.

Die weiteste Verbreitung hat hier sicherlich Smalltalk erfahren, das Anfang der 70er Jahre die Programmierer im Xerox Palo Alto Research Center aus der Zielsetzung heraus entwickelten, eine ausdrucksstarke ergonomische Benutzerschnittstelle zu schaffen. Smalltalk ist der Ursprung vieler Komponenten grafischer Benutzeroberflächen, die heute als selbstverständlich gelten wie Mause, Fenstertechnik, Pull-down und Pop-up-Menüs.

Nachdem 1981 ein ganzes Heft des Computermagazins Byte ausschließlich Smalltalk gewidmet war, richtete sich zwar das Augenmerk der breiteren Öffentlichkeit auch auf die objektorientierte Software-Entwicklung, aber bis zum endgültigen Durchbruch der Idee verging noch einmal fast ein Jahrzehnt. Auch heute noch haben viele objektorientierte Sprachen eher experimentellen Charakter, doch gewinnen die kommerziell eingesetzten zunehmend an Bedeutung.