orientierte Programmierung (OOP) 1. Motivation Die objektorientierte Sichtweise der Welt Als Motivation für die OOP sieht man sich am besten die reale Welt an: Die reale Welt besteht aus "en", z. B.: Gegenstände, Häuser, Maschinen, Fahrzeuge, Personen, Tiere, Pflanzen, etc. Diese e sind relativ selbständige Einheiten. Sie haben meist einen bestimmten Zustand und erfüllen bestimmte Aufgaben. Die e haben ein Innenleben, das wir aber normalerweise nicht verstehen müssen, um sie sinnvoll einzusetzen, und das vielfach gar nicht bekannt ist. Es genügt zunächst, das äußere Verhalten eines e zu kennen, zu wissen, welche Aufgabe es erfüllt. Um die Welt (äußerlich) zu verstehen, muss man die Aufgaben der verschiedenen e kennen und wissen, wie sie miteinander kommunizieren und interagieren. Übertragung auf die Programmierung Es liegt nahe, die objektorientierte Sichtweise auf Programme zu übertragen: Ein Programm besteht aus en, das sind selbständige Teile, die für bestimmte Aufgaben zuständig sind. Jedes enthält bestimmte Informationen, die seinen augenblicklichen Zustand repräsentieren. Diese Daten werden durch Variablen dargestellt. Diese nennt man in der OOP Attribute. Die Aufgaben und Aktionen eines s werden durch Funktionen dargestellt. Diese heißen in der OOP Methoden oder Operationen. Die Zusammenfassung von Attributen und Operationen zu en (Kapselung) ist ein wichtiges Prinzip der OOP. Man unterscheidet: - den internen Aufbau eines s - die nach außen hin sichtbaren Eigenschaften (Schnittstelle, Interface) eines s, sowie seine Funktionalität nach außen Nur letzteres ist für das Verständnis des Zusammenwirkens von en notwendig. Wie ein seine Aufgaben erfüllt ist seine interne Angelegenheit (Implementation) Das Verstecken interner Informationen (Verbergen von Informationen, information hiding) ist ein weiteres wichtiges Prinzip der OOP. 1
Durch die objektorientierte Vorgehensweise ist es besser als mit der traditionellen Methode der Programmierung möglich, komplexe Programme überschaubar zu halten. 1 3 2 Anwendungen Die OOP lässt sich in fast allen Bereichen der Programmierung einsetzen, insbesondere bei komplexen Projekten. Besonders erfolgreich wird sie eingesetzt bei Grafischen Benutzeroberflächen (GUI) Simulationen orientierte Analyse und Entwurf (Design) Besonders wichtig sind bei der OOP die Analyse der Problemstellung und das Programmdesign. Diese umfassen die Zerlegung der Aufgabe in e (Klassen), die Festlegung der die Schnittstellen der e und die Beziehungen der e untereinander. Der richtige Entwurf kann über den Erfolg des ganzen Projekts entscheiden und erfordert sehr viel Erfahrung. Deshalb gibt es schon seit Langem verschiedene grafische Methoden für systematische Analyse und Design (die zusammen Modellierung genannt werden). 1997 entwickelten verschiedene Gruppen zusammen eine vereinheitlichte Modellierungs-Sprache, die sogenannte Unified Modeling Language (UML). Diese umfangreiche Sprache umfasst zahlreiche Diagrammtypen, von denen sich einige speziell mit den statischen und dynamischen Eigenschaften von Klassen und en befassen. 2
2. Aufbau von en Traditionelle Programmierung (strukturiert, imperativ): In der strukturierten Programmierung liegt das Hauptaugenmerk auf den Algorithmen. Diese verwenden Daten, die in Form von Variablen im Programm enthalten sind. Daten getrennt von Algorithmen Problem: Funktionen können auf Daten zugreifen, die nicht für sie bestimmt sind. Daten 1 Funktionen 1 Daten 2 Funktionen 2 orientierte Programmierung: Kapselung In der OOP werden Daten (= Attribute) und Algorithmen (in Form von Funktionen = Operationen) zu en zusammengefasst. Dies ist ein wichtiges Prinzip der OOP. Man nennt es Kapselung. Meist ist damit auch gemeint, dass es nicht möglich ist, von außen auf die Daten eines s zuzugreifen, sondern nur mit Hilfe geeigneter Operationen (s. Verbergen von Informationen) 1 2 Attribute Attribute Operationen Operationen 3
Der Unterschied in zwei Wörtern Man könnte den Unterschied zwischen traditioneller strukturierter Programmierung und objektorientierter Programmierung wie folgt auf den Punkt bringen: Die strukturierten Programmierung stellt die Frage "Wie?" (d. h. die Frage nach dem Algorithmus). Die OOP stellt die Frage "Was?" (d. h. die Frage: was für e brauche ich?). Sichtbarkeit von Attributen und Operationen (Verbergen von Informationen): orientierte Programmiersprachen bieten die Möglichkeit, Informationen zu verbergen (information hiding). Dadurch ist es möglich, zwischen einem privaten und einem öffentlichen Teil des s zu unterscheiden. Nur der öffentliche Teil ist für den Benutzer eines s relevant. Er bildet die Schnittstelle nach außen. ( Attribute ) privat (private) unsichtbar (Implementation) Attribute Operationen Operationen öffentlich (public) sichtbar (Interface) 4
e und Klassen Neben dem Begriff "" tritt in der OOP auch der Begriff "Klasse" auf. Während ein etwas Konkretes ist, das Speicherplatz belegt und zu jedem Zeitpunkt einen bestimmten Zustand hat, ist eine Klasse etwas Abstraktes: Sie ist ein Muster, das als Vorlage für die Erzeugung von en dient, so wie ein Datentyp das Muster für eine Variable ist. D. h., eine Klasse ist ein Datentyp und ein, das nach dem Muster dieser Klasse erzeugt wird, ist eine Variable von diesem Datentyp. Man spricht vom als der Instanz der Klasse (Instanzvariable). Datentyp Variable Klasse Datentyp = Muster oder Vorlage für Variable Klasse = Muster oder Vorlage für = Instanz (eigentlich Exemplar) einer Klasse Eine Klasse belegt normalerweise keinen Speicherplatz. Ein benötigt dagegen Speicherplatz um seinen internen Zustand abzuspeichern. 3. Vererbung Von einer Klasse kann eine andere Klasse abgeleitet werden. Dies bedeutet: Die neue Klasse übernimmt (erbt) alle Eigenschaften der Ursprungsklasse (alle Attribute und alle Operationen), kann aber zusätzliche Eigenschaften erhalten. Man bezeichnet die neue Klasse als Unterklasse (sub class) der Ursprungsklasse, die Ursprungsklasse als Oberklasse (super class) der neuen Klasse. Dieser Mechanismus der Ableitung von Klassen, auch Vererbung genannt, ist ein wichtiges Prinzip der OOP. 5
Beispiel: Oberklasse Ort, Geschwindigkeit Masse, Größe beschleunige () bremse () Klasse Fahrzeug erweitert / ist abgeleitet von Unterklasse Zusätzlich: Tankfüllung Drehzahl zündungan () lenke () schalte () Klasse Auto Zu beachten ist dabei, dass jede Instanz der Klasse "Auto" auch gleichzeitig eine Instanz der Klasse "Fahrzeug" ist, da ein Auto alle Eigenschaften eines Fahrzeugs besitzt. Die Klassen in der OOP spielen also eine ähnliche Rolle wie Klassifizierungen von en im realen Leben. Dabei spielt die Relation "ist abgeleitet von" bei den Klassen die gleiche Rolle wie die Relation "ist ein" bei den realen en. 6
Beispiel Zoologie: Wirbeltiere Reptilien Fische Säugetiere Krokodile Katzen Hunde Hier gilt z. B.: Jede Katze ist ein Säugetier, jedes Säugetier ist ein Wirbeltier, aber auch: jede Katze ist ein Wirbeltier. Analog gilt bei Klassen in der OOP: wenn B Unterklasse von A ist und C Unterklasse von B, dann ist C auch Unterklasse von A (und A ist Oberklasse von C). Normalerweise hat eine Klasse nur eine Oberklasse. In manchen Programmiersprachen (wie z. B. C++) gibt es allerdings auch die Möglichkeit der Mehrfachvererbung (eine Klasse ist von mehreren Klassen abgeleitet). 4. Polymorphie Ein weiteres Prinzip der OOP ist die sogenannte Polymorphie (das heißt übersetzt Vielgestaltigkeit). Darunter ist zu verstehen, dass man gleichartige Operationen auf e verschiedener Klassen anwenden kann. So kann man eine Klasse für komplexe Zahlen erstellen und dafür eine Operation + definieren. Man kann aber auch eine Klasse für Zeichenketten erstellen und dafür ebenfalls einen +-Operator definieren, der Zeichenketten verkettet. Somit steht die Operation + je nach Kontext für verschiedene Operationen. Dies gibt es auch schon bei elementaren Datentypen. Z. B. steht der Divisionsoperator / bei reellen Zahlen für einen normale Division, bei ganzen Zahlen für eine Ganzzahldivision. Meist tritt die Polymorphie in Zusammenhang mit der Vererbung auf. So kann man z. B. eine Oberklasse "Form" definieren, von der die Unterklassen "Kreis", "Quadrat" und "Dreieick" abgeleitet sind. Man kann in der Klasse "Form" eine Operation "zeichne()" definieren, die dann erst in den Unterklassen konkretisiert wird. Hat man nun verschiedene konkrete Form-e (also Kreise, Quadrate, Dreiecke) so kann man einfach für alle Form-e die Funktion zeichne() aufrufen. Es werden dann automatisch die korrekten Operationen der Unterklassen ausgeführt. 7
5. Einige Design-Prinzipien in der orientierung Neben den bisher genannten Prinzipien der OOP gibt es noch Prinzipien, an denen man sich beim Entwurf objektorientierter Software orientieren sollte: Prinzip einer einzigen Verantwortung: Statt Verantwortung könnte man auch Zuständigkeit sagen. Jede Klasse sollte eine klar definierte Zuständigkeit besitzen. Erleichtert die Aufteilung eines Programms in Klassen. Wiederholungen vermeiden: Damit ist nicht gemeint, dass keine Schleifen programmiert werden sollen, sondern, dass nicht gleichartige Code-Stücke an mehreren Stellen des Programms auftreten sollten. Allgemeiner ausgedrückt: Eine bestimmte Funktionalität sollte in einem Programm nur an einer Stelle umgesetzt werden. Ist dies nicht der Fall, so sollte eine geeignete Funktion eingesetzt werden. Erleichtert die Wartbarkeit von Software, macht sie übersichtlicher und verhindert die künstliche Aufblähung von Programmen. Offen für Erweiterungen, geschlossen für Änderungen: Dies bedeutet, dass eine Klasse so flexibel programmiert werden sollte, dass sie sich später an erweiterte Aufgaben anpassen lässt (z. B. durch Vererbung), ohne die alte Funktionalität zu verändern. Gewährleistet die Erweiterbarkeit der Software. Trennung von Schnittstelle und Implementierung: es sollte klar getrennt werden, welche Funktionalität eine Klasse nach außen zur Verfügung stellt und wie das intern bewerkstelligt wird. Erleichtert die Austauschbarkeit einer Klasse in einem Programm. Testbarkeit: Zu jeder Klasse sollte es einen Testmodul geben, der Funktionalitäten der Klasse testet (sog. Unit-Tests). Ermöglicht schon vor Fertigstellung eines kompletten Programms die einzelnen Klassen zu testen, aus denen das Programm aufgebaut ist. 8