Oft genannte Vorteile der OO-Programmierung:
Allerdings:
Daher:
Moderne OO-Sprachen kommen mit
So kann man
tatsächlich mit OOP einfach und schnell zu guten Ergebnissen kommen.
Die Komplexität der Probleme, die man lösen kann, hängt vom Grad der Abstraktion ab, den die verwendete Programmiersprache bietet:
Imperative Programmiersprachen
Funktionale und logische Programmiersprachen
Man modelliert das zu lösende Problem.
Diese Ansätze sind gut für ihren jeweils typischen Anwendungsbereich.
Außerhalb oft unpassend.
Objektorientierte Programmiersprachen
Der Programmierer beschäftigt sich mit Elementen aus der Problemstellung ( Objekten) und ihrer Darstellung im Lösungsraum. Die Anwendbarkeit ist nicht auf einen bestimmten Typ von Problemen beschränkt.
,,Reine`` objektorientierte Sprachen lassen sich wie folgt
charakterisieren1:
Die Schnittstelle ( interface) eines Objektes legt fest, welche Botschaften ein Objekt verarbeiten kann.
In Java sieht dies wie folgt aus
class Motor { public void on(){ ... } public void off(){ ... } public void faster(){ ... } public void slower(){ ... } }
So erzeugt man einen Motor und macht ihn an:
Motor mo = new Motor(); mo.on();
Die Schnittstelle stellt die Charakterisierung eines Objektes für die Öffentlichkeit dar. Versteckt sind (i.d.R.)
Wenn die Schnittstelle sich nicht ändert, kann beides ausgetauscht
werden, ohne die Anwendung zu tangieren.
Wiederverwendung und Wiederverwendbarkeit von Klassen ist eines der Schlüssenkonzepte der objektorientierten Programmierung.
Zwei Formen:
Einfachste Art der Wiederverwendung. Eine Klasse wird wiederverwendet, indem Objekte dieser Klasse als Elemente in anderen Klassen benutzt werden.
class Auto { Motor m; Rad r[]; ... }
Vererbung schafft neue Klassen, die von ihrer Elternklasse alle Elemente und die Schnittstelle erben.
class Diesel extends Motor { ... }
Durch die Vererbung der Schnittstelle kann man an die Unterklasse die
gleichen Botschaften schicken wie an die Oberklasse. Da der Typ eines
Objekts definiert ist durch die Botschaften, die das Objekt versteht,
folgt, daß Unter- und Oberklassenobjekte den gleichen Typ haben
( Typäquivalenz).
Einige Sprachen definieren eine ausgezeichnete Klasse, die Oberklasse aller Klassen ist. Diese Klasse ist die Wurzel der Klassenhierarchie.
Eiffel:
Vererbung allein bringt noch nichts, da Unter- und Oberklassen nicht nur die gleiche Schnittstelle sondern auch das gleiche Verhalten haben.
Zwei Möglichkeiten:
class Diesel extends Motor { public void glüh() { ... } // Nur für Diesel }
class Diesel extends Motor { public void on() // überschreibt "on" aus "Motor" { glüh(); super.on(); } }
Je nachdem, ob die erbende Unterklasse nur Methoden überschreibt oder auch Methoden hinzufügt, haben wir zwei Ausprägungen der Beziehung:
Vererbung führt bei einfacher Erbung ( single inheritance) zu baumförmigen Klassenhierarchien.
Die wichtigste Eigenschaft von Klassenhierarchien:
Polymorphie heißt Vielgestaltigkeit:
Motor m = new Elektro(); .... m = new Diesel();
Polymorphie bedeutet:
Beispiel:
void motor_check(Motor m) { m.on(); // Motor-Diagnosen m.off(); } ... Diesel d = new Diesel(); Elektro e = new Elektro(); motor_check(d); motor_check(e);
Die Formulierung von motor_check() ist typunabhängig:
Nicht
Sondern
m.on() ist der Aufruf der on()-Methode, die für den jeweils gültigen Typ von m zuständig ist (z. B. on() aus Diesel).
Welcher Typ dies ist, steht erst zur Laufzeit des Programmes fest.
Der Name on im Aufruf m.on() kann erst dynamisch an eine
der verschiedenen on-Ausprägungen gebunden werden.
Statischer Speicher und Keller:
Heap:
C++
Motor *mo = new Diesel(); ... mo->on();
Java
Motor mo = new Diesel(); ... mo.on();
Objektkopie
Objekte werden bei Referenzsemantik immer
über Referenzen manipuliert.
a = b
wird also nur eine Referenz kopiert und kein Wert.
Kopie von Werten erreicht man mit
Vector b = new Vector(); c = b.clone();
c verweist auf ein Duplikat von b.
Voraussetzung: Vector implementiert die Methode clone().
Objekte werden bei Referenzsemantik immer
über Referenzen manipuliert.
swap(a,b);
werden also nur Objektreferenzen übergeben.
Vorsicht: Dies hat nichts mit Call-by-Reference zu tun, wo
Adressen von Variablen übergeben werden.
Bei Referenzsemantik der Parameterübergabe funktioniert folgende
swap-Funktion NICHT:
public void swap(Object a, Object b) { Object tmp; tmp = a; a = b; b = tmp; }
da sie keine Effekte auf ihre aktuellen Parameter ausübt.
Container:
Beispiel:
Schlangen, Bäume, Hash-Tabellen, u.v.a.
Container sind i.a. nicht Element der Sprache sondern Teil von
Bibliotheken
(C++: STL, Java: API, Delphi VCL)
Enumeration e = table.elements(); while (e.hasMoreElements()) doSomethingWith(e.nextElement());
So lassen sich z.B. Container für einen Typ von Inhalt spezialisieren:
C++
template <class T> class Queue { ... } ... Queue<Person> qp;
Java hat keine generischen Typen.
Abhilfe:
Container des allgemeinsten Typs Object.
Motor mo; // ein Motor Queue q; // eine Schlange von Objects q.enq(mo); // mo in die Schlange
Nachteile:
Um entnommene Objekte verwenden zu können, brauchen wir
Typanpassung entgegen der Typhierarchie
( Downcast)!
mo = (Motor) q.deq(); // raus aus der Schlange
Gefahr:
Java liefert in diesem Fall eine Laufzeit-Exception.
Die notwendige Prüfung kostet Laufzeit.