Und jetzt zu etwas ganz anderem
Beim Programmieren gibt es mehrere unterschiedliche Grundansätze. Was
Ihr bisher kennengelernt habt, war der prozedurale
Ansatz. Dabei sind im Speicher des Computers die Daten einerseits
(Variablen) und die Anweisungen andererseits (das eigentliche Programm)
zwei getrennte Bereiche. Die Realität ist jedoch etwas anders konstruiert:
Wenn Ihr Euch mal umschaut, seht Ihr im wesentlichen
Objekte, die Eigenschaften haben und
Funktionen ausführen können.
Nehmen wir als Beispiel eine gewöhnliche Lampe. Das ist zunächst
mal ein Objekt. Dieses Objekt hat Eigenschaften
- das Licht ist an oder aus
- die Birne hat eine bestimmte Stärke (Wattzahl)
- die Farbe der Glühbirne
- normale Glühbirne oder Halogenlampe
- ...
und Funktionen
- Licht ein- oder ausschalten
- melden, ob die Glühbirne durchgebrannt ist
- Glühbirne wechseln
- ...
Wäre es nicht schön, diese Prinzipien einfach in ein Programm
übernehmen zu können?
Variable l sei eine Lampe;
Gebe die Wattzahl von l aus;
wenn (l ist ausgeschaltet)
schalte l ein;
Genau das ermöglicht die objekt-orientierte
Programmierung (OOP).
Lamp l = new Lamp();
print l.strength;
if (!l.isEnlighted())
l.enlight();
Die allermeisten heutigen Programmiersprachen bieten zumindest
objekt-orientierte Ansätze oder sind komplett objekt-orientiert. Das
gilt auch für die Sprachen, die wir für dynamische Webseiten
brauchen. Daher kann ich Euch zumindest die Grundprinzipien der OOP nicht
ersparen.
Die Variable l im obigen Beispiel enthält ein
Objekt. Dieses Objekt besitzt Eigenschaften in Form von Variablen.
Auf diese Variablen könnt ihr in den meisten Sprachen mit dem
Punktoperator zugreifen
// Zugriff auf die Eigenschaft 'strength' des Objekts 'l'
print l.strength;
l.strength = 60; // Neuer Wert für die Eigenschaft
Ebenso besitzt das Objekt Funktionen, die Ihr aufrufen könnt
l.enlight(); // rufe die Funktion 'enlight()' des Objekts 'l' auf
Diese Eigenschaften und Funktionen sind in der jeweiligen
Klasse definiert, in diesem Fall in der Klasse
'Lamp'. Die Klasse ist sozusagen das «allgemeine Prinzip 'Lampe'»,
die Objekte sind die konkreten Lampen, die auf dem Schreibtisch stehen.
Ihr könnt Euch die Klasse als Variablen-Typ der Objekte vorstellen.
Daher werden Objektvariablen auch mit dem Klassenamen deklariert
// deklariert eine Variable, die ein Objekt
// der Klasse 'Lamp' aufnehmen kann
Lamp l;
Ein neues Objekt müßt Ihr erst erzeugen.
Dabei wird, wie bei der Deklaration einer einfachen Variablen, der
notwendige Speicherplatz reserviert. Erzeugt wird ein Objekt mit dem
Schlüsselwort new
Lamp l; // Deklaration
// erzeuge ein neues Objekt
// der Klasse 'Lamp' in der Variablen l
l = new Lamp();
Lamp ll = new Lamp(); // es geht auch in einer Zeile
Was hinter dem Schlüsselwort 'new' folgt, ist eine besondere Funktion
in der Klasse, genannt Konstruktor. Wie bei
anderen Funktionen auch, können auch Konstruktoren überladen
werden. Das bedeutet, es kann Konstruktoren mit unterschiedlichen Parametern
geben
var l = new Lamp(); // Konstruktor ohne Parameter
l = new Lamp(60); // mit 60-Watt-Birne
l = new Lamp(true); // die Lampe ist gleich eingeschaltet
l = new Lamp(60, true); // beides auf einmal
Welche Eigenschaften, Konstruktoren und andere Funktionen die Klassen einer
Programmiersprache bieten, steht ebenfalls im Manual. Ihr könnt auch
selbst Klassen definieren und benutzen, aber das würde den Rahmen
dieses Tutorials sprengen. Wichtig ist nur, daß Ihr die vorhandenen
Klassen benutzen könnt, insbesondere bei der clientseitigen Sprache
JavaScript.
Objektorientierte Merkwürdigkeiten
Bei der Arbeit mit Objekten müßt Ihr vor allem einen Unterschied
zu einfachen Variablen beachten. Das folgende Beispiel soll deutlich machen,
was gemeint ist
var a = 0; // erzeuge zwei Variablen
var b = 1;
b = a; // weise b den Wert zu, den a hat
a = 3; // weise a einen neuen Wert zu
print a; // gebe die beiden Werte aus
print b;
Soweit noch nichts sonderlich überraschendes: a hat den Wert 3 und b
hat den Wert 0. Aber wenn wir das ganze jetzt mit Objekten machen, sieht
es etwas anders aus
Lamp a = new Lamp(60); // erzeuge zwei Objekte
Lamp b = new Lamp(100);
b = a;
a.strength = 90; // setze Eigenschaft neu
print a.strength; // gebe die Eigenschaft der beiden Objekte aus
print b.strength;
Für das Objekt in a wird jetzt 90 ausgegeben, und für das Objekt
in b - auch? Wieso das?
Das liegt daran, daß in einer Objektvariablen nicht das Objekt selbst
gespeichert wird, sondern nur ein Verweis auf das
Objekt. Das bedeutet, durch die Zuweisung 'b = a' verweisen sowohl die
Variable a als auch b auf dasselbe Objekt! Dessen Eigenschaft wird mit
'a.strength = 90' neu gesetzt, und dann abgefragt. Und weil eben a und b
auf dasselbe Objekt verweisen, wird auch beidesmal dasselbe ausgegeben.
Dieses Prinzip ist nicht ganz leicht zu verstehen. Es hilft, wenn Ihr Euch
das ganze einfach mal aufmalt
Beispiel mit einfachen Variablen
Beispiel mit Objekten
Das führt außerdem noch zu einer anderen Merkwürdigkeit:
Beim OOP müßt Ihr unterscheiden zwischen
Gleichheit und Identität. Zwei Objekte
sind gleich, wenn alle Eigenschaften den gleichen Wert haben. Sie sind
aber nicht identisch, weil es eben zwei getrennte Objekte sind. Das
könnt Ihr Euch vorstellen wie zwei Stück Papier, auf denen der
gleiche Text steht. Diese beiden Zettel sind gleich, aber nicht identisch.
Der bekannte Vergleichsoperator '==' prüft bei Objekten auf Identität.
Zum Test auf Gleichheit kennen viele Klassen eigene Funktionen, z. B.
equals() oder
compareTo().
Ich hatte vorhin geschrieben, daß alle Eigenschaften und Funktionen
zu einem Objekt gehören müssen. Das ist prinzipiell richtig, aber
es gibt Ausnahmen. Solche Eigenschaften und Funktionen existieren einmal
für die gesamte Klasse. Man nennt sie
statische Eigenschaften oder Funktionen.
Statische Eigenschaften oder Funktionen müssen bei der Definition in
der Klasse als solche gekennzeichnet sein. Das geschieht meist mit dem
Schlüsselwort static. Häfig steht
dieses 'static' auch im Manual, um statische Elemente kenntlich zu machen.
Verwenden könnt Ihr statische Elemente, indem Ihr statt eines Objekts
einfach den Klassennamen angebt. Als Beispiel nehmen wir eine Klasse namens
'Integer', die eine statische Funktion namens 'parseInt(String)' kennt
String in = input(); // hole Eingabe vom User als String
// benutze statische Funktion, um aus dem String ein int zu machen
int i = Integer.parseInt(in);
Ihr braucht also kein Objekt der Klasse Integer, sondern könnt die
Funktion «einfach so» benutzen.
Zur OOP gäbe es noch viel mehr zu sagen, aber das würde hier zu
weit führen. Daher nur noch ein paar Stichworte
- Vererbung
- Polymorphie
- abstrakte Klassen und Interfaces
- Geheimnisprinzip und Zugriffsbeschränkungen
- Fehlerbehandlung und Exceptions
- Ereignisse und Listener
- ...
Tips und Tricks
Damit haben wir jetzt endlich das grundsätzliche Rüstzeug, um
uns in die Programmierung dynamischer Webseiten zu stürzen. Vorher noch
schnell ein paar Tips, die besonders bei der Fehlersuche nützlich sind
- Drittel-Prinzip
-
In vielen Lehrbüchern wird die Entwicklung eines Programms in drei
Teile aufgeteilt: Ein Drittel Planung, ein Drittel Programmieren und
ein Drittel Fehlersuche. Und für jeden dieser Teile ist etwa
gleich viel Zeit zu veranschlagen. Für Euch bedeutet das: Vor dem
eigentlichen Code-Schreiben solltet Ihr Euch bereits über eine
Reihe von Fragen Gedanken gemacht haben, wie beispielsweise
- Wo und in welchem Format speichere ich meine Daten?
- Wie soll mein Programm grundsätzlich aufgebaut sein?
- Wie soll die Benutzerführung aussehen?
- ...
Wenn Ihr Euch das während des Code-Schreibens auch noch
überlegen müßt, verliert Ihr leicht den Überblick.
Und nach dem Programmieren beginnt das Testen. Dabei solltet Ihr nicht
nur «normale» Fälle überprüfen, sondern auch
mal absichtlich Fehler machen: Was passiert, wenn ich keine Nummer
für das anzuzeigende Bild eingebe? Was passiert, wenn ich ein
Bild anfordere, das auf dem Server gar nicht existiert? Was passiert,
wenn ich während des Ladens eines Bildes ein neues anfordere? Was
passiert, wenn ...?
- Bleistift-Test
-
Gerade bei komplizierteren Codes ist es nicht ganz einfach, logische
Fehler zu finden. In solchen Fällen hilft oft der Bleistift-Test.
Ihr nehmt einen großen Zettel und vollzieht das Programm Zeile
für Zeile nach. Welche Werte haben die Variablen, wie oft werden
Schleifen ausgeführt, wie wird an if-Verzweigungen entschieden,
...? Dadurch findet man eigentlich immer zumindest das Problem,
häufig läßt die Idee für die Lösung auch nicht
lange auf sich warten. Es gibt übrigens auch Programme, mit denen
Ihr solche Bleistift-Tests am Rechner machen könnt. Die nennt man
Debugger.
- 'Hier bin ich!'
-
Eine Variante des Bleistift-Tests ist ein Verfahren, das ich 'Hier bin
ich!' nenne. Dabei baut Ihr einfach an wichtigen Stellen kleine Ausgaben ein
print "Jetzt werden die Daten geladen!";
...
print "Jetzt werden die Daten sortiert!";
...
print "Jetzt werden die Daten ausgegeben!";
...
Damit seht Ihr, woran der Rechner jeweils gerade werkelt, und wo es
«hängt». Ihr könnt nach dem gleichen Prinzip
auch Variablenwerte ausgeben und schauen, ob sie wie gewünscht
belegt werden.
|