4.2 Grundlagen zu eigenen Klassen
In der objektorientierten Programmierung gilt das Paradigma: Programmiere eine Klasse, erstelle viele Objekte. Der Vorteil dieser Vorgehensweise ist die Wiederverwendbarkeit: Anstatt jedes Objekt einzeln zu programmieren, kann ein Bauplan genutzt werden, um viele Objekte dieser Klasse zu erzeugen. Der Bauplan selbst muss nur einmal implementiert werden.
Die Siedlung am Wald ist ein gutes Beispiel für die Sinnhaftigkeit dieses Vorgehens: Die Siedlung besteht aus mehreren Häusern und Bäumen. Man könnte nun wie im Projekt Landschaft vorgehen und den Quelltext zur Erstellung des Hauses vervielfältigen (z.B. per Copy & Paste) und jeweils die Position der einzelnen Bausteine wie Dach und Tür anpassen. Allerdings birgt das Vorgehen enorme Nachteile:
- Wenn das Haus verändert werden soll, indem beispielsweise die Farbe oder die Größe der Fenster modifiziert wird, muss dies an vier Stellen statt an einer Stelle erfolgen, um alle Häuser zu ändern. Neben dem Aufwand ist hier vor allem die Fehleranfälligkeit von Nachteil.
- Der Quelltext wird deutlich länger und damit schlechter lesbar.
Stattdessen macht es Sinn, eine Klasse Haus
und eine Klasse Baum
zu erstellen. Von diesen Klassen können dann viele Objekte erzeugt werden.

Hinweis
Wenn beim Progammieren intensiv Copy & Paste genutzt wird, kann das ein Anzeichen dafür sein, dass vom objektorientierten Ansatz der Programmierung abgewichen wird.
Prinzipien der objektorientierten Programmierung
Beim Klassenentwurf verfolgt man in der objektorientierten Programmierung zudem die beiden folgenden Prinzipien:
Prinzip der verteilten Intelligenz / Autonomitätsprinzip: Objekte sollen sich selbst verwalten, wann immer es möglich ist. Sie werden bestenfalls nicht „von außen“ gesteuert. Zur Kommunikation mit anderen Objekten stellen sie Methoden zur Verfügung.
Datenkapselung / Geheimnisprinzip: Eine Klasse soll nur so viel über ihre Wirkungsweise „veröffentlichen“, wie zur Erfüllung ihrer Aufgaben nötig ist. Was vor der Außenwelt verborgen werden kann, soll verborgen werden.

Klassenentwurf
Zur Modellierung der Siedlung am Wald greifen wir wieder auf die Fragen zurück, die wir bereits beim Erstellen der ersten Landschaft verwendet haben.
-
- Die erste Frage lautet: Aus welchen Objekten besteht diese Klasse? Für die Klasse
Baum
lässt sich das schnell beantworten: Ein Baum besteht aus einerkrone
, die als Objekt der KlasseSGKreis
modelliert werden kann, und einemstamm
, also einem Objekt der KlasseSGRechteck
. - Die zweite Frage zielt auf den Konstruktor der Klasse: Mit welchen Anfangswerten müssen die Objekte erzeugt werden? Diese Frage ist schwieriger zu beantworten. Beim Erzeugen eines
Baum
-Objekts sind Farbe und Größe für alle Objekte gleich, sie können im Konstruktor mit den entsprechenden Anweisungen festgelegt werden. Allerdings unterscheiden sichBaum
-Objekte in ihrer Position, die jedemBaum
-Objekt individuell zugewiesen werden muss.
Der Konstruktor der KlasseBaum
muss also so gewählt werden, dass die Position des Baumes als Parameter übergeben werden kann. Im Konstruktor werden dann die Parameter genutzt, um den Baum zu positionieren. - Die dritte Frage lautet: Wie müssen die Objekte nach ihrer Erzeugung verändert werden? Wenn es beispielsweise möglich sein soll, die Bäume zu fällen, muss die Klasse
Baum
eine MethodefaelleBaum()
implementieren, in derkrone
undstamm
um 70° gedreht werden.
- Die erste Frage lautet: Aus welchen Objekten besteht diese Klasse? Für die Klasse
Damit gelangt man zum nebenstehenden UML-Implementationsdiagramm.



Implementation der Klasse
Die Klasse Baum
kann auf der Grundlage des UML-Diagramms implementiert werden. Der Großteil der Anweisungen ist bereits aus vorherigen Projekten bekannt. Interessant mit Blick auf die Verwendung eigener Klassen sind die Zeilen 5, 6 und 9 des folgenden Quelltextes.
public class Baum {
private SGRechteck stamm;
private SGKreis krone;
public Baum(int pX, int pY) {
krone = new SGKreis(pX,pY,40);
krone.setzeFuellung(true);
krone.setzeFarbe(0.1,0.9,0.5);
stamm = new SGRechteck(pX+30,pY+40,20,80);
stamm.setzeFuellung(true);
stamm.setzeFarbe(0.7,0.7,0.1);
}
public void faelleBaum() {
double tempX = stamm.gibX() + stamm.gibBreite()*0.5;
double tempY = stamm.gibY() + stamm.gibHoehe();
stamm.dreheUmPunkt(70, tempX, tempY);
krone.dreheUmPunkt(70, tempX, tempY);
}
}
Bei der Zeile 5 handelt es sich um den Konstruktor. Anders als bei bisher programmierten Klassen finden sich hier in den Klammern Parameter. Dabei handelt es sich um Variablen, denen beim Aufruf des Konstruktors ein Wert zugewiesen werden muss. Sie dienen also als Platzhalter, um dynamisch Werte beim Erzeugen eines Objekts festzulegen.
In unserem Fall wird erst beim Erzeugen eines Baum
-Objekts die Position festgelegt, da diese sich für alle Baum
-Objekte unterscheiden soll. Entsprechend finden sich im Konstruktor zwei Parameter, einmal pX
für die x-Position und pY
für die y-Position.
Diese beiden Variablen werden auch in den Anweisungen in Zeile 6 und 9 genutzt, wenn die Position von krone
und stamm
festgelegt wird. Die Werte in pX
und pY
werden dabei genutzt, um die Position der Objekte zu berechnen.
Die Erzeugung eines Baums erfolgt in der Klasse Landschaft
zum Beispiel mit der folgenden Anweisung:
meinBaum = new Baum(500,200);
Beim Ausführen des Konstruktors werden nun die Parameter pX
und pY
mit den Werten 500 und 200 gefüllt. Die Variablen pX
und pY
stehen nun innerhalb des Konstruktors zur Verfügung: An jeder Stelle, an der diese Variablen genutzt werden, werden zur Laufzeit die angegebenen Werte eingesetzt. Die Anweisung in Zeile 14 wird für das obige Beispiel zu:
stamm = new SGRechteck(500+30,200+40,20,80);
