Zum Inhalt springen

Objektorientierte Programmierung

aus Wikipedia, der freien Enzyklopädie
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 7. Juni 2005 um 22:42 Uhr durch Mh26 (Diskussion | Beiträge) (Literatur: info erg). Sie kann sich erheblich von der aktuellen Version unterscheiden.

Objektorientierte Programmierung (OOP) ist ein Verfahren zur Strukturierung von Computerprogrammen, bei dem zusammengehörige Daten und die darauf arbeitende Programmlogik zu Einheiten zusammengefasst werden, den sogenannten Objekten.

Zumindest konzeptionell arbeitet ein Programm dann nicht mehr (wie bei der prozeduralen Programmierung) so, dass sequentiell einzelne Funktionsbereiche eines Algorithmus durchlaufen werden, der dabei eine Anzahl Daten verändert, sondern die Programmlogik entfaltet sich in der Kommunikation und den internen Zustandsveränderungen der Objekte, aus denen das Programm aufgebaut ist.

Vorteile der objektorientierten Programmierung liegen in der besseren Modularisierung des Codes, dadurch bedingt in einer höheren Wartbarkeit und Wiederverwendbarkeit der Einzelmodule, sowie in einer höheren Flexibilität des Programmes insgesamt, insbesondere in Bezug auf die Benutzerführung, da Programme dieser Art weniger stark gezwungen sind, dem Benutzer bestimmte Bedienabläufe aufzuzwingen.


Konzepte der objektorientierten Programmierung

Grundlegende Begriffe

Im Folgenden werden wichtige Begriffe der objektorientierten Programmierung kurz umrissen. Für nähere Details sei auf die jeweiligen Einzelabschnitte und -artikel verwiesen.

Die einzelnen Bausteine, aus denen ein objektorientiertes Programm während seiner Abarbeitung besteht, werden, wie bereits erwähnt, als Objekte bezeichnet. Die Konzeption dieser Objekte erfolgt dabei in der Regel auf Basis der folgenden Paradigmen:

Abstraktion
Jedes Objekt im System kann als ein abstraktes Modell eines Akteurs betrachtet werden, der Aufträge erledigen, seinen Zustand berichten und ändern und mit den anderen Objekten im System kommunizieren kann, ohne offen legen zu müssen, wie diese Fähigkeiten implementiert sind (vgl. abstrakter Datentyp (ADT)).
Kapselung
Objekte können den internen Zustand anderer Objekte nicht in unerwarteter Weise lesen oder ändern. Ein Objekt hat eine Schnittstelle, die darüber bestimmt, auf welche Weise mit dem Objekt interagiert werden kann. Dies verhindert das Umgehen von Invarianten des Programms.
Polymorphie
Verschiedene Objekte können auf die gleiche Nachricht unterschiedlich reagieren. Wird die Zuordnung von Nachricht zur Reaktion auf die Nachricht erst zur Laufzeit aufgelöst, dann wird dies auch späte Bindung (oder dynamische Bindung) genannt.
Vererbung
Neue Arten von Objekten können auf der Basis bereits vorhandener Objekt-Definitionen festgelegt werden. Es können neue Bestandteile hinzugenommen werden oder vorhandene überlagert werden. Wird keine Vererbung zugelassen, so spricht man zur Unterscheidung oft auch von objektbasierter Programmierung.

Klassen

Zur besseren Verwaltung gleichartiger Objekte bedienen sich die meisten Programmiersprachen des Konzeptes der Klasse. Klassen sind Vorlagen, aus denen Objekte zur Laufzeit erzeugt werden. Im Programm werden dann nicht einzelne Objekte, sondern eine Klasse gleichartiger Objekte definiert.

Man kann sich die Erzeugung von Objekten aus einer Klasse vorstellen wie das Fertigen von Autos aus dem Konstruktionsplan eines bestimmten Fahrzeugtyps. Klassen sind die Konstruktionspläne für Objekte.

Die Klasse entspricht in etwa einem komplexen Datentyp prozeduraler Programmierumgebungen, geht aber darüber hinaus: Sie legt nicht nur die Datentypen fest, aus denen die mit Hilfe der Klassen erzeugten Objekte bestehen, sie definiert zudem die Algorithmen, die auf diesen Daten operieren. Während also zur Laufzeit eines Programmes einzelne Objekte miteinander interagieren, wird das Grundmuster dieser Interaktion durch die Definition der einzelnen Klassen festgelegt.

In manchen Programmiersprachen gibt es außerdem zu jeder Klasse wiederum ein bestimmtes Objekt, das so genannte Klassenobjekt, das dazu da ist, die Klasse selbst zur Laufzeit zu repräsentieren. Dieses Klassenobjekt ist dann auch zuständig für die Erzeugung der Objekte der Klasse und den Aufruf des passenden Algorithmus.

Klassen werden in der Regel in Form von Klassenbibliotheken zusammengefasst, die häufig thematisch organisiert sind. So können Anwender einer objektorientierten Programmiersprache Klassenbibliotheken erwerben, die beispielsweise den Zugriff auf Datenbanken ermöglichen.

Methoden

Die einer Klasse von Objekten zugeordneten Algorithmen bezeichnet man auch als Methoden.

Häufig wird der Begriff Methode synonym zu Funktion oder Prozedur gebraucht, obwohl die Funktion oder Prozedur eher als Implementierung einer Methode zu betrachten ist. Im täglichen Sprachgebrauch sagt man auch "Objekt A ruft Methode m von Objekt B auf."

Eine besondere Rolle spielen Methoden für die Kapselung, insbesondere auch als Zugriffsfunktionen. Spezielle Methoden zur Erzeugung bzw. "Zerstörung" von Objekten heißen Konstruktoren und Destruktoren.


Kapselung

Als Kapselung bezeichnet man den kontrollierten Zugriff auf Objekte. Vom Innenleben eines Objektes soll der Verwender (gemeint sind sowohl die Algorithmen, die mit den Objekten arbeiten, als auch der Programmierer, der diese entwickelt) möglichst wenig wissen müssen (Geheimnisprinzip). Durch die Kapselung werden nur Informationen über das "Was" eines Objektes (was es leistet) nach außen sichtbar, nicht aber das "Wie" (die interne Repräsentation). Dadurch wird eine Schnittstelle nach außen definiert und zugleich dokumentiert.

Anmerkung: Das Kapselungsprinzip gibt es auch unabhängig von objektorientierten Konzepten, z.B. ist eine Deklaration von Daten nur innerhalb eines Programmmoduls ebenfalls eine Form von Kapselung.

Für die Kapselung verwendete Zugriffsarten

Die UML als De-facto-Standardnotation erlaubt die Modellierung folgender Zugriffsarten (in Klammern die Kurznotation der UML):

public (+)
zugreifbar für alle Ausprägungen (auch die anderer Klassen),
private (-)
Nur für Ausprägungen der eigenen Klasse zugreifbar,
protected (#)
Nur für Ausprägungen der eigenen Klasse und von Spezialisierungen der selben zugreifbar,
package (~)
erlaubt den Zugriff für alle Elemente innerhalb des eigenen Pakets.

Die Möglichkeiten zur Spezifizierung der Zugreifbarkeit sind je nach Programmiersprache unterschiedlich.

Vorteile der Kapselung

  • Dadurch dass die Implementierung eines Objektes anderen Objekten nicht bekannt ist, kann die Implementierung geändert werden, ohne die Zusammenarbeit mit anderen Objekten zu beeinträchtigen.
  • Erhöhte Übersichtlichkeit, da nur die öffentliche Schnittstelle eines Objektes betrachtet werden muss.
  • Es wird verhindert, dass innere Zusammenhänge, die möglicherweise später einmal verändert werden, Änderungen in anderen Programmteilen erfordern.
  • Beim lesenden Zugriff über eine Zugriffsfunktion spielt es von außen keine Rolle, ob diese Funktion 1:1 im Inneren des Objekts existiert, das Ergebnis einer Berechnung ist, oder möglicherweise aus anderen Quellen (z.B. einer Datei oder Datenbank) stammt.
  • Oft stehen mehrere Einzelinformationen in einem Zusammenhang. Durch Verändern dieser über eine Zugriffsfunktion muss man sich darüber keine Gedanken machen, da man erwarten darf, dass diese sich um die Konsistenz dieser Daten kümmert.
  • Deutlich verbesserte Testbarkeit, Stabilität und Änderbarkeit der Software.

Vererbung

Prinzip

Die Definition eines neuen Objektes kann gegebenenfalls auf der Definition eines bereits vorhandenden Objektes aufsetzen, so dass das neue Objekt die Merkmale des vorhandenen übernimmt und um neue Bestandteile ergänzt. Die Übernahme der Merkmale des vorhandenen Objektes bezeichnet man als Vererbung.

Überdeckt ein neues Merkmal ein bei der Vererbung übernommenes Merkmal, dann spricht man von Überschreiben.

Die Nutzung der Vererbung bietet sich an, wenn es Objekte gibt, die konzeptionell aufeinander aufbauen. Gegebenenfalls lassen sich Objektdefinitionen von vorneherein so aufteilen, dass identische Merkmale in der Definition eines "vererbenden" Objektes zusammengefasst werden.

Polymorphie

Unter bestimmten Voraussetzungen können Algorithmen, die auf den Schnittstellen eines bestimmten Objekttypes operieren, auch mit davon abgeleiteten Objekten zusammenarbeiten.

Geschieht dies so, dass durch Vererbung überschriebene Methoden an Stelle der Methoden des vererbenden Objektes ausgeführt werden, dann spricht man von Polymorphie.

Vererbung und das Klassenkonzept

Vererbungsbeziehungen zwischen Objekten werden in der Regel mit Hilfe von Klassendefinitionen hergestellt. Dabei bezeichnet man die "vererbende" Klasse als Basisklasse oder auch Superklasse und die "erbende" Klasse als abgeleitete Klasse bzw. Subklasse.

Eine besondere Bedeutung haben dabei abstrakte Klassen. Eine abstrakte Klasse definiert Methoden und Eigenschaften deren Implementierung in dieser Klasse noch nicht sinnvoll aber für daraus abgeleitete Klassen notwendig ist(Schnittstelle). So könnte im Beispiel eine Methode "Zeichnen" in der Basisklasse "Fahrzeug" noch als abstrakt deklariert werden. Die konkrete Implementierung obliegt dann den daraus abgeleiteten Subklassen.

Beispiel

Ein Fahrzeug besitzt bestimmte Attribute, diese können z.B. Höchstgeschwindigkeit oder maximale Zuladung oder auch Farbe sein. Die Klasse Kraftfahrzeug erbt all diese Attribute, kann aber noch zusätzliche Attribute besitzen, z.B. Leistung oder Drehmoment. Des Weiteren kann ein Kraftfahrzeug auch zusätzliche Methoden wie Beschleunige() besitzen, welche die Basisklasse Fahrzeug nicht kennt.

Die Klasse Personenkraftwagen kann dann wiederum von Kraftfahrzeug abgeleitet werden und weitere zusätzliche Attribute wie Anzahl der Sitze besitzen. Durch die Ableitung von Kraftfahrzeug erbt der Personenkraftwagen automatisch alle Attribute von Fahrzeug.

Ob eine Klasse in einer Vererbungsbeziehung zu einer anderen Klasse steht, lässt sich durch eine einfache "Ist-Ein"-Regel feststellen. Sobald die Aussage

"die abgeleitete Klasse ist eine Basisklasse"

zutrifft, stehen beide Klassen in einer Vererbungsbeziehung.

  • Ein Personenkraftwagen ist ein Kraftfahrzeug,
  • ein Personenkraftwagen ist ein Fahrzeug, aber
  • ein Fahrzeug ist kein Personenkraftwagen.
  • Ein Personenkraftwagen ist auch kein Sitz, sondern
  • ein Personenkraftwagen besitzt einen oder hat einen Sitz.

Hat einen bezeichnet also Attribute einer Klasse.

Mehrfachvererbung

UML Klassendiagramm einer Mehrfachvererbung
UML Klassendiagramm einer Mehrfachvererbung

Von Mehrfachvererbung spricht man, wenn eine Klasse mehrere unmittelbare Basisklassen hat, die nicht voneinander erben. Die Möglichkeit der Mehrfachvererbung besteht nur in wenigen Programmiersprachen (z.B. C++, Python; auch die kommende Version von Ada soll Mehrfachvererbung unterstützen).

Ein Anwendungsbeispiel hierfür ist die Modellierung eines Amphibienfahrzeugs, das sowohl die Attribute von Landfahrzeug als auch die von Wasserfahrzeug erbt. Damit besitzt Amphibienfahrzeug sowohl eine Räderzahl als auch einen Tiefgang.

Nicht alle objektorientierten Programmiersprachen erlauben die Mehrfachvererbung. Programmiersprachen mit Mehrfachvererbung sind z.B. C++, Eiffel und Python. Dagegen unterstützen Smalltalk und Ada Mehrfachvererbung nicht. Als Einwand gegen Mehrfachvererbung wird häufig genannt, dass es das Design unnötig kompliziere und undurchsichtig machen könne.

Java, Delphi und C# bieten mit so genannten Schnittstellen eine eingeschränkte Form der Mehrfachvererbung. Hierbei kann eine Klasse maximal von einer Basisklasse abgeleitet werden, jedoch kann Sie beliebig viele Schnittstellen erben. Damit verpflichtet sich diese Klasse, die Methoden der Schnittstelle zu erfüllen. Mit einfacher Vererbung und Schnittstellen sind die meisten Anforderungen an ein Software-Design realisierbar, ohne die Nachteile der Mehrfachvererbung in Kauf nehmen zu müssen.

Akquisition

Ein Sonderfall der Vererbung ist die von der Webanwendung Zope realisierte Akquisition: Hierbei erbt ein Objekt nicht aufgrund seiner Klasse von seiner Superklasse (is-a- bzw. Ist-Ein-Beziehung), sondern durch seine Position in einer Objekthierarchie (ist enthalten in). Dies ist nützlich z. B. für die Generierung dynamischer Internet-Auftritte: 'oben' im Objektbaum (d. h. nahe der Wurzel) definierte Eigenschaften oder Methoden werden an alle Objekte vererbt, die weiter 'unten' in dem betreffenden Unterbaum liegen.

Auf das Beispiel eines Autos übertragen, könnte man sich vorstellen, dass das Auto die Eigenschaft Farbe und vier Radkappen hat; die Radkappen sind Teil des Autos (bzw. 'darin enthalten') und erben von ihrem Container, dem Auto, die Eigenschaft Farbe.

Bezeichnungen

Die Begriffe der objektorientierten Programmierung haben teilweise unterschiedliche Namen. Folgende Bezeichnungen werden synonym verwendet:

Bezeichnungen in der objektorientierte Programmierung
Superklasse = Basisklasse = Oberklasse
Subklasse = abgeleitete Klasse = Unterklasse
Methode = Elementfunktion = Memberfunktion
Attribut = Datenelement = Member
aus einer Klasse erzeugtes Objekt = Exemplar = Instanz

Programmiersprachen

Grundsätzlich kann man durch strikte Einhaltung bestimmter Regeln in den meisten Programmiersprachen objektorientiert programmieren. Jedoch erleichtern und fördern speziell hierfür ausgerichtete objektorientierte Programmiersprachen dies ungemein.

Die erste bekannte objektorientierte Programmiersprache ist Simula-67. Später wurden die Prinzipien dann in Smalltalk weiter ausgebaut. Die heute bekanntesten objektorientierten Sprachen sind C++, Java und C#.

Die meisten objektorientierten Programmiersprachen erlauben es, verschiedene Programmiertechniken miteinander zu kombinieren. Manchmal werden dabei bestimmte Prinzipien der objektorientierten Programmierung durchbrochen. Beispielsweise handhaben viele Programmiersprachen das Prinzip der Kapselung nicht ganz so streng und stellen es dem Entwickler anheim, wie stark er bestimmte Konzepte wie beispielsweise die Kapselung objektinterner Daten durch Zugangsmethoden einhält oder nicht.

In rein objektorientierten Sprachen wie Smalltalk werden, dem Prinzip alles ist ein Objekt folgend, auch elementare Typen wie Ganzzahlen (Integer) durch Objekte repräsentiert. Auch Klassen selbst sind hier Objekte, die wiederum Ausprägungen von Metaklassen sind. Viele Sprachen, unter anderem C++ und Java folgen allerdings nicht der reinen Lehre der Objektorientierung; daher sind dort elementare Typen keine vollwertigen Objekte, sondern müssen auf Methoden und Struktur komplett verzichten.

In einigen objektorientierten Programmiersprachen wie zum Beispiel JavaScript, NewtonScript und Self wird auf die Deklaration von Klassen gänzlich verzichtet. Statt dessen werden bestehende Objekte, so genannte Prototypen abgeleitet. Die Attribute und Methoden des Prototypen kommen immer dann zum Einsatz, wenn sie nicht im abgeleiteten Objekt explizit überschrieben wurden. Dies ist vor allem für die Entwicklung kleinerer Programme von Vorteil, da es einfacher und zeitsparend ist.

Einige objektorientierte Programmiersprachen:

Programmierparadigmen

Die objektorientierte Programmierung gehört zu den so genannten Programmierparadigmen. Nach dem objektorientierten Programmierparadigma werden Objekte mit Daten und den darauf arbeitenden Routinen zu Einheiten zusammengefasst. Im Unterschied dazu werden beim so genannten prozeduralen Paradigma die Daten von den darauf arbeitenden Routinen getrennt gehalten.

Geschichte der objektorientierten Programmierung

Denkweise und Begriffe der objektorientierten Programmierung zeigten sich zuerst in Simula, einer Sprache für Simulationszwecke, die als erste Sprache (damals noch nicht so genannte) objektorientierte Verfahren einführte.

Diese Begriffe und Verfahren wurden später bei Xerox PARC mit der Sprache Smalltalk verfeinert. Entwickelt wurde diese erste Smalltalk-Version in Simula nun als voll dynamisches System, bei dem man Objekte interaktiv erzeugen und ändern konnte – im Gegensatz zum vorher verwendeten System statischer Programme.

Die objektorientierte Programmierung begann Mitte der 1980er Jahre populärer zu werden, hauptsächlich durch den Einfluss von C++, das sich dem Programmierer gegenüber gewissermaßen als syntaktische Erweiterung der Sprache C ausgibt. Weiter gefestigt wurde die Stellung der objektorientierten Programmierung durch die schnell wachsende Beliebtheit der grafischen Bedienoberflächen, die sich objektorientiert sehr gut programmieren lassen.

Seit dieser Zeit wurden für viele existierende Programmiersprachen objektorientierte Erweiterungen geschaffen, z.B. für Ada, BASIC, LISP, Pascal und andere. Das Hinzufügen dieser Erweiterungen zu Sprachen, die ursprünglich nicht für objektorientierte Programmierung entworfen wurden, kann zu Problemen mit der Kompatibilität und Wartbarkeit von Code führen. "Rein" objektorientierten Sprachen wiederum fehlen gewisse prozedurale Programmiermöglichkeiten, an die sich viele Programmierer inzwischen gewöhnt hatten. Um diese Lücke zu schließen, wurden verschiedene Versuche unternommen, neue objektorientierte Sprachen zu schaffen, die gleichzeitig "sichere" prozedurale Programmierung erlauben. Die Programmiersprache Eiffel war einer der ersten einigermaßen erfolgreichen Versuche in dieser Richtung, wurde inzwischen aber praktisch von Java verdrängt. Die an Java und C++ angelehnte Sprache C# verfolgt ähnliche Ziele wie Java.

Umfeld

So wie die Techniken der prozeduralen Programmierung durch Verfahren wie die strukturierte Programmierung verfeinert wurden, so gibt es inzwischen auch Verfeinerungen der objektorientierten Programmierung durch Methoden wie Entwurfsmuster (englisch design patterns), Design by Contract (DBC) und grafische Modellierungssprachen wie UML. Einen immer wichtigeren Stellenwert nimmt die aspektorientierte Programmierung ein, bei dem Aspekte von Eigenschaften und Abhängigkeiten beschrieben werden. Erste Ansätze sind beispielsweise in Java mit J2EE oder der abstrakten Datenhaltung über Persistenz-Layer sichtbar.

Siehe auch

Literatur

  • Harold Abelson, Gerald Jay Sussman, Julie Sussman: Structure and Interpretation of Computer Programs. The MIT Press, ISBN 0262011530
  • Grady Booch: Object-Oriented Analysis and Design with Applications. Addison-Wesley, ISBN 0805353402
  • Peter Damann: Objektorientierte Programmierung mit Delphi. Klett Verlag, ISBN 3-12-738700-8
  • Peter Eeles, Oliver Sims: Building Business Objects. John Wiley & Sons, ISBN 0-471-19176-0
  • Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Design Patterns: Elements of Reusable Object Oriented Software. Addison-Wesley, ISBN 0201633612
  • Paul Harmon, William Morrissey: The Object Technology Casebook. Lessons from Award-Winning Business Applications. John Wiley & Sons, ISBN 0-471-14717-6
  • Ivar Jacobsen: Object-Oriented Software Engineering: A Use-Case-Driven Approach. Addison-Wesley, ISBN 0201544350
  • Bertrand Meyer: Object-Oriented Software Construction. Prentice Hall, ISBN 0136291554
  • Bernd Oestereich: Objektorientierte Programmierung mit der Unified Modeling Language. Oldenbourg, ISBN 3-486-24319-5
  • James Rumbaugh, Michael Blaha, William Premerlani, Frederick Eddy, William Lorensen: Object-Oriented Modeling and Design. Prentice Hall, ISBN 0136298419
  • David A. Taylor: Object-Oriented Information Systems. Planning and Implementation. John Wiley & Sons, ISBN 0-471-54364-0