Objektorientierung verstehen AnPr Name 1 Klasse Datum Das Klassenkonzept in der realen Welt Im realen Leben wenden wir den Gedanken der Klassen – oder besser der Klassifizierung –unbewusst an. Wir wissen, dass ein Hund und ein Vogel Tiere sind. Tiere sind wiederum genau wie Pflanzen Lebewesen; wir haben also eine Klasse als Subklasse einer andeLebewesen ren identifiziert. Alle Elemente einer Klasse zeichnen Geburtsdatum sich durch gemeinsame Eigenschaften und FähigkeiGewicht ten aus. So sind Tiere in der Lage sich aus eigener Kraft fortzubewegen. Alle Lebewesen werden geboren und sterben, um nur einige Gemeinsamkeiten zu Pflanze Tier nennen. Standort Grafisch können diese Beziehungen in verschiedenster Weise dargestellt werden – im IT Bereich wird dies üblicherweise mit UML (Unified Modeling Language) durchgeführt. Hier wird ein Pfeil auf die darüber liegende Klasse gezeichnet; man spricht von einer Generalisierung. Bewegungsgeschwindigk. Hund Anzahl Zähne Vogel Schnabelgröße In der Programmierung wurde dieses Konzept als sinnvoll erkannt und in die Designs von neuen Objektorientierten Programmiersprachen einbezogen. Moderne höhere Programmiersprachen bedienen sich in aller Regel am Konzept der Objektorientierung, so wie Java, C++ und C#. Die Idee, die dahinter steckt ist, Programmstrukturen zu schaffen, welche Eigenschaften und Fähigkeiten aufweisen – man spricht hier von Attributen und Methoden. Diese zeichnen jeweils eine Klasse (einer Programmstruktur) aus. Wir werden im Nachgang noch sehen, was dies im Einzelnen bedeutet. Für die Einführung dieses Konzeptes in eine Programmiersprache, bedienen wir uns der Geometrie. Ausgehend vom Geometrieobjekt hangeln wir uns immer tiefer in die einzelnen möglichen Klassen: Geometrieobjekt Farbe Fläche Geo3DObjekt Geo2DObjekt Höhe Umfang Kreis Radius Dreieck Länge a Länge b Länge c Viereck Länge Breite Für die weiteren Übungen werden wir uns an diesem Konzept orientieren. Im Laufe der nächsten Schritte werden noch weitere Eigenschaften und auch Fähigkeiten der einzelnen Klassen identifiziert und umgesetzt. Bis dahin bleiben wir jedoch bei dem hier gezeigten Schema. ANPR_01_Objektorientierung_v01.docx Seite 1 Objektorientierung verstehen AnPr 2 Das Klassenkonzept bei Programmiersprachen 2.1 Allgemeines Programme sind im Regelfall dazu gedacht, Daten auf verschiedenste Art und Weise einzulesen, zu verarbeiten und auszugeben. Bis jetzt wurde dies in einfachen (Unter-) Programmen realisiert, ohne auf Klassen oder ähnliche Konzepte einzugehen. Diese Art der Programmierung nennt man „prozedural“, da hier die gesamte Ausführung in Prozeduren umgesetzt wurde. Der Objektorientierte Gedanke besagt nun, dass nicht mehr Unterprogramme die Daten verarbeiten, sondern die Daten selbst bringen Methoden mit, welche für die Anpassung von Datenwerten herangezogen werden. Um dies zu verdeutlichen, soll folgendes überlegt werden. Gesetz dem Fall, dass man programmiertechnisch ein Konstrukt geschaffen hat – nennen wir es Kreis – welches als Eigenschaft den Radius besitzt. Der Programmierer möchte nun eine Funktionalität umsetzen, die Fläche auszurechnen. Er hat nun die Möglichkeit eine Funktion zu schreiben, welche den Kreis als Parameter aufnimmt, den Radius erfragt und die Fläche berechnet: Wie im rechten Struktogramm gezeigt, muss das berechnende Programm zuerst die Eigenschaften des Objektes kennen, bevor es die Berechnung durchführen kann. Jedes geometrische Objekt hat eine eigene Formel für die Flächenberechnung. Sehr viel praktischer wäre es nun, dass das Objekt selbst die Berechnungsformel mitbringt. Das Struktogramm würde somit wie folgt aussehen: Das Objekt ist selbstständig in der Lage, die Flächenberechnung durchzuführen. Der Nutzer dieser Objekte muss sich nicht mehr um die einzelnen Berechnungsformeln kümmern, sondern er holt sich lediglich die Eigenschaft ab, bzw. lässt sie berechnen. Seite 2 AnPr Objektorientierung verstehen 2.2 Klassendefinitionen in Java Die Frage ist nun, wie das in Java umgesetzt wird. Folgender Code ist ein Beispiel für die Deklaration einer Klasse: Was: Syntax einer Variablendeklaration public class Geometrieobjekt { } Programmiersprache: Java Erklärung: public Sichtbarkeit (siehe nächstes Kapitel) class Hinweis, dass eine Klasse deklariert wird Geometrieobjekt Name der Klasse Programmierstilhinweis1: Namensgebung Wie bei Variablen auch, sollen die Klassen sprechende Namen erhalten. Es ist üblich Klassennamen mit einem Großbuchstaben beginnen zu lassen. Die oben gezeigte Klasse besitzt noch keine Eigenschaften und keine Methoden. Um dies nun zu ermöglichen, wird die Fläche und Farbe als Eigenschaft implementiert und die Methoden getColor() und setColor() vorgesehen: Was: Syntax einer Variablendeklaration Programmiersprache: Java public class Geometrieobjekt { protected double dFlaeche; private int iFarbe; public void setFarbe(int iNeueFarbe) { this.iFarbe = iNeueFarbe; } public int getFarbe() { return this.iFarbe; } } Erklärung: public, private, Sichtbarkeit (siehe nächstes Kapitel) protected dFlaeche, iFarbe Klassenvariablen setFarbe, getFar- Methoden der Klasse be this. Referenz auf Klassenvariable, wobei this. immer für das aktuelle Objekt steht. Auf die Objektinstanziierung wird weiter unten eingegangen. Programmierstilhinweis: Sichtbarkeit und Bei der Deklaration von Eigenschaften und Methoden ist immer die Sichtbarkeit anzuGetter/Setter geben. Hier ist darauf zu achten, dass nur diejenigen Eigenschaften und Methoden Methoden sichtbar gemacht werden, welche auch vom außenstehenden Nutzer zu verwenden sind. Auf die Sichtbarkeit wird unten näher eingegangen. Im Regelfall sollten Klassenvariablen private sein und nur über Getter/Setter nach außen geführt werden. Die Klasse hat nun zwei Eigenschaften und zwei Methoden. Wichtig zu verstehen ist nun, dass im Regelfall 2 eine Klasse „nur“ eine Definition, ähnlich eines Bauplanes ist. Um nun die Methoden einer Klasse nutzen zu 1 Diese sind keine Industrieweite Normung. Im Rahmen des Unterrichts wird hierauf Wert gelegt, in Ihrem Betrieb werden wahrscheinlich nicht die gleichen, aber ähnliche Regelungen gelten. 2 Bei statischen Methoden/Attributen gilt dies nicht Seite 3 Objektorientierung verstehen AnPr können, muss ein Objekt dieser Klasse instanziiert werden. In einem Testprogramm wird nun das Objekt instanziiert und das Verhalten des Objektes der Klasse geprüft: Was: Syntax einer Variablendeklaration Programmiersprache: Java public class GeoTest { public static void main(String[] args) { Geometrieobjekt meinGeoobjekt = new Geometrieobjekt(); meinGeoobjekt.setFarbe(283214); System.out.println(meinGeoobjekt.getFarbe()); } } Erklärung: main Geometrieobjekt meinGeoobjekt new Geometrieobjekt() Einstiegspunkt für Virtual Machine Klassentyp für folgende Variable Variable für Objekt der Klasse Geometrieobjekt Schlüsselwort für die Instanziierung eines Objektes (Standard)Konstruktor der Klasse Geometrieobjekt. Das obige Beispiel zeigt nun, dass innerhalb der Main Methode ein Objekt der Klasse Geometrieobjekt deklariert und anschließend instanziiert wird. Mittels des Punkt-Operators kann nun auf die Eigenschaften und Methoden der Klasse zugegriffen werden, welche als public deklariert wurden. Zugriff auf private Eigenschaften und Methoden ist an dieser Stelle nicht möglich. 2.3 Sichtbarkeitskennungen / Zugriffsebenen Das Schlüsselwort public ist seit dem ersten HelloWorld Programm ein Begriff, wenngleich die Hintergründe dieses Schlüsselwortes bis dato nie eine Rolle gespielt haben. An dieser Stelle soll die Bedeutung der einzelnen Möglichkeiten nun kurz dargestellt werden: Schlüsselwort: private protected public Bedeutung: Die Methode/Eigenschaft kann nur innerhalb der Klassendefinition angesprochen werden. Die Methode/Eigenschaft kann nur innerhalb der Klassendefinition, oder einer vererbten Klassendefinition angesprochen werden. Die Methode/Eigenschaft kann überall angesprochen werden. Somit kann also gesteuert werden, ob ein Programm, welches die Klasse instanziiert, die Methoden verwenden kann oder nicht. Man hat somit die Möglichkeit ein definiertes Interface an den Programmierer zu übergeben, welcher die Klasse nutzen möchte (daher auch der Name API – Application Programming Interface). Es zeugt von gutem Programmierstil, wenn man Klasseneigenschaften grundsätzlich als privat oder protected deklariert und die Zugriffe von außen ausschließlich über Getter- und Setter-Methoden realisiert, wie es bei der Klasseneigenschaft „iFarbe“ realisiert wurde. Der Vorteil von dieser Methodik ist, dass man vor allem beim Setzen eines Wertes über eine Setter-Methode diese bspw. mit Validitätsprüfungen versehen kann (bspw. wenn eine Gradzahl von 380 angegeben wird, kann dies auf einen Wert zwischen 0° und kleiner 360° korrigiert werden). Ein weiteres, wichtiges Schlüsselwort ist static. Wie oben geschildert, sind die Eigenschaften und Methoden einer Klasse nur dann nutzbar, wenn auch ein Objekt instanziiert wurde. Es gibt jedoch Situationen, in denen man eine Funktionalität nutzen möchte, ohne ein Objekt zu instanziieren. Dies wurde beispielsweise bei Integer.parseInt() benötigt. Man kann die Methode parseInt() der Klasse Integer nutzen, ohne ein Objekt der Klasse Integer zu instanziieren. Seite 4 AnPr Objektorientierung verstehen Dieses Verhalten kann mit der Eigenschaft static realisiert werden: Was: Syntax einer Variablendeklaration Programmiersprache: public class Geometrieobjekt { public static String sVersion = "1.0.0"; … } Java public class GeoTest { public static void main(String[] args) { System.out.println(Geometrieobjekt.sVersion); } } Erklärung: static Zugriffsmöglichkeit via Klasse Üblicherweise sind Klasseneigenschaften, welche als static implementiert wurden als konstant definiert (Schlüsselwort final), da Klassen im Regelfall intern nicht verändert werden sollen. 2.4 Vererbung von Eigenschaften und Methoden Klassen haben sich unter anderem durch gemeinsame Eigenschaften ihrer Subklassen definiert. Um dies programmtechnisch zu realisieren, wurde der Mechanismus der Vererbung geschaffen. Wir wollen nun ein 2DObjekt erzeugen, welches jedoch Eigenschaften vom Geometrieobjekt übernimmt: Was: Syntax einer Variablendeklaration Programmiersprache: Java public class Geo2DObjekt extends Geometrieobjekt { private double dUmfang; public void setdUmfang(double dUmfang) { this.dUmfang = dUmfang; } public double getdUmfang() { return dUmfang; } public boolean flaecheGroesserAls(double dVergleichsflaeche) { return (super.dFlaeche > dVergleichsflaeche); } } Erklärung: extends Schlüsselwort indiziert, dass die Klasse die Eigenschaften einer Superklasse erbt. super. Zugriff auf eine Variable der Superklasse, welche die Eigenschaften vererbt. Dies ist im Regelfall „nur“ dann notwendig, wenn innerhalb der Klasse eine Variable deklariert wurde, welche den gleichen Namen hat, wie die der Superklasse. In dem genannten Beispiel wäre ein gleichbedeutender Code: return (dFlaeche > dVergleichsflaeche); bzw.: return (this.dFlaeche > dVergleichsflaeche); Sämtliche public und protected Eigenschaften und Methoden der Klasse Geometrieobjekt sind nur auf die Klasse Geo2DObjekt übergegangen. Seite 5 Objektorientierung verstehen AnPr 2.5 Überschreiben von Methoden Es kommt mitunter vor, dass in der Superklasse Methoden implementiert wurden, welche in der abgeleiteten Klasse in der Form nicht zu einem richtigen Ergebnis führen würden. Solche Methoden können in der abgeleiteten Klasse überschrieben werden. Folgendes Beispiel soll dies verdeutlichen. Gehen wir davon aus, dass in der Klasse Geometrieobjekt folgende Methode implementiert wurde: public double berechneFlaecheNeu() { return this.dFlaeche; } Danach wurde von der Klasse Geo2DObjekt eine weitere Klasse vererbt, namens Rechteck. Diese Klasse, soll in der Lage sein, die Fläche neu zu berechnen und zurückzugeben: public class Rechteck extends Geo2DObjekt { private double dLaenge = 2; private double dBreite = 3; public double berechneFlaecheNeu() { this.dFlaeche = this.dLaenge * this.dBreite; return this.dFlaeche; } } Wenn nun ein Rechteck Objekt instanziiert wird, so wird beim Aufruf der Methode berechneFlaecheNeu() die überschriebene Methode ausgeführt und nicht die ursprüngliche der Superklasse. Mehr noch – wenn nun ein Rechteck Objekt in einer Variable vom Typ Geometrieobjekt abgelegt wird, so würde bei Aufruf der Methode berechneFlaecheNeu() ebenfalls die überschriebene Methode ausgeführt werden: public class GeoTest { public static void main(String[] args) { Geometrieobjekt meinRechteck = new Rechteck(); System.out.println(meinRechteck.berechneFlaecheNeu()); } } Dieses Verhalten ermöglicht es nun, jedem Geometrieobjekt eine individuelle Neuberechnung der Fläche zu implementieren, diese aber einheitlich über Variablen des Typs Geometrieobjekt aufzurufen. Es können also beliebige geometrische Objekte erzeugt werden (Kreis, Rechteck, Dreieck …), bei denen alle eine individuelle Methode berechneFlaecheNeu() implementiert wurde. Egal welche Instanz erzeugt wurde (Kreis, Rechteck, Dreieck usw.) und in einer Variable mit dem Typ Geometrieobjekt gespeichert wurde, der Aufruf variablenname.berechneFlaecheNeu() wird immer die Implementierung des richtigen Objektes aufrufen. In dem Oben geschilderten Fall – dass eine Methode in der Elternklasse „leer“ erzeugt wird und die sinnvolle Implementierung erst in den abgeleiteten Klassen erfolgt – spricht man von „abstrakten Methoden“. Abstrakt steht also dafür, dass die Implementierung nur eine Art Platzhalter darstellt, welche erst bei den „Kindklassen“ mit Funktionen versehen werden. Darüber hinaus kommt es jedoch durchaus vor, dass die Elternklassen eine funktionierende Implementierung aufweisen, welche für die abgeleiteten Klassen keinen Sinn ergeben und dort somit neu erstellt werden. Seite 6 AnPr Objektorientierung verstehen 2.6 Überladen von Methoden Unter Überladung versteht man, dass ein Methodenname mehrfach mit unterschiedlicher Signatur vorkommt - also mit unterschiedlichen Parametern aufgerufen wird. Hier ein Beispiel: public class Rechteck extends Geo2DObjekt { private double dLaenge = 2; private double dBreite = 3; public double berechneFlaecheNeu() { this.dFlaeche = this.dLaenge * this.dBreite; return this.dFlaeche; } public void setFlaeche(double dFlaeche) { this.dFlaeche = dFlaeche; } public void setFlaeche() { this.berechneFlaecheNeu(); } } Im Beispiel wird die Methode setFlaeche zwei Mal umgesetzt, einmal mit dem Parameter dFlaeche und einmal ganz ohne Parameter. Je nachdem welche Signatur beim Aufruf verwendet wird, führt die Virtual Machine die eine, oder andere Methode aus. Die Signatur wird somit bestimmt durch die Anzahl und den Datentypen der Übergabeparameter. 2.7 Konstruktoren Wie in den obigen Beispielen dargestellt, werden Objekte mittels des Schlüsselworts new und einem Konstruktor instanziiert. Der Standardkonstruktor, welcher von Java immer akzeptiert wird, ist gleichnamig mit der Klasse und eine sich öffnenden und schließenden Klammer: new Geometrieobjekt(); Dieser kann jedoch auch überschrieben werden: public Geometrieobjekt() { this.iFarbe = 0; } Insofern können eventuelle Initialisierungen vorgenommen werden. Auch kann ein Konstruktor überladen werden, indem bspw. ein Übergabeparameter vorgesehen wird: public Geometrieobjekt(int iFarbe) { this.iFarbe = iFarbe; } 2.8 Destruktoren In manchen objektorientierten Programmiersprachen (bspw. C++) gibt es sogenannte Destruktoren. Bei Aufruf dieser werden Abschlussarbeiten durchgeführt und anschließend alle Ressourcen des Objektes freigegeben. In Java gibt es dies nicht, da die Ressourcenfreigabe über den Garbage Collector automatisch erfolgt, wenn keine Referenz mehr auf das Objekt existiert. Seite 7 Objektorientierung verstehen 3 AnPr Lizenz Diese(s) Werk bzw. Inhalt von Maik Aicher (www.codeconcert.de) steht unter einer Creative Commons Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 3.0 Unported Lizenz. Seite 8