In diesem Abschnitt werden wir uns mit den objektorientierten Erweiterungen von PERL beschäftigen, und eine eigene kleine Objekthierarchie aufbauen. Nahziel dieser kurzen Einführung in die objektorientierte Programmierung ist es, ein solides Verständnis für die Techniken bei der Anwendung von Objektbibliotheken zu entwickeln, mit dem wir in den Kapiteln über CGI und Netzwerkprogrammierung die vielen leistungsfähigen freien PERL-Objektbibliotheken optimal nutzen können.
Seit Version 5.0 implementiert PERL einige Features der objektorientierten Programmierung:
Nicht untersützt werden abgestufte Zugriffsberechtigungen auf Klassenmethoden und --felder, also Konstrukte wie public,protected,private,... Dies ist der Philosophie von Larry Wall geschuldet, der dem Programmierer den größtmöglichen Spielraum lassen möchte, und damit im Gegensatz zu der verbreiteten Philosopie steht Programmiersprachen so zu entwerfen, dass der Programmierer zu einem vermeintlich guten Stil gezwungen werden soll.
Die objektorientierten Erweiterungen die mit Version 5 Einzug in PERL gehalten haben basieren auf einigen simplen Erweiterungen der Package/Hashtable Konzepte, so dass objektorientiertes Programmieren in PERL praktisch möglich ist, ohne mit den Begriffen "Klasse" oder "Objekt" konfrontiert zu werden.
Im wesentlichen sind die drei OOP-Integredienzen Objekte Klassen und Vererbung implementiert:
bless realisiert.
Hashes haben wir bisher nur als Ansammlung skalarer key-value-Paare betrachtet. Da der "Value" eines solchen Pares aber auch eine Referenz auf eine Liste, einen Hash, oder eine Subroutine sein kann, so wird deutlich, dass hier noch beträchtliches Potential zur Erzeugung komplexerer Datenstrukturen exisitert.
Im folgenden Beispiel speichern wir einen Skalar, eine Liste, einen Hash, und schließlich noch ein Stück Code in einem Hash ab:
#
# Abspeichern...
#
$myhash{"memberscalar"}="ein Scalar";
$myhash{"memberarray"}=[1,2,3,4];
$myhash{"memberhash"}={"key1"=>"Val1","key2"=>"Val2", "key3"=>"Val3" };
$myhash{"memberfunc"}= sub{ print "\n myfunc \n"; };
#
# Wiedergewinnen...
#
print "\n";
print $myhash{"memberscalar"};
print "\n";
print @{$myhash{"memberarray"}};
print "\n";
print %{$myhash{"memberhash"}};
&{$myhash{"memberfunc"}};
Das obige Beispiel hat gezeigt, dass ein Hash in der Lage ist in der gleichen Weise als strukturierter Speicher für Datensätze zu dienen wie Records, Unions, Structs,... in anderen Programmiersprachen. Hinzu kommt die Fähigkeit Code zu verwalten, die von keiner der oben aufgezählten Konstrukte geboten wird. Kleinliche Programmierer könnten diesem Konzept allerdings einige Schwächen attestieren:
Mithilfe einiger Erweiterungen des Packagekonzeptes können diese Problem seit PERL 5 aber umgangen werden.
Im folgenden Codefragment wird ein Package angelegt, das
nur eine Funktion mit Namen new enthält.
New legt einen leeren Hash mit Namen "self" an,
anschließend wird der Hash mit Referenzen auf ein Array, einen Hash und eine
Subroutine gefüllt. Schließlich wird der Hash zurückgeliefert:
#
package mPack;
#
sub new($\@\%\&) {
shift; # das erste Element der Parameterliste ("mPack") wird weggeworfen
my $self = {}; # einen leeren Hash, und eine Referenz darauf anlegen
$self->{"myScalar"}=shift; # ein k-v-Feld fü den Scalar anlegen
$self->{"myList"} =shift; # ein k-v-Feld fü die Liste anlegen
$self->{"myHash"} =shift; # ein k-v-Feld fü den Hash anlegen
$self->{"myFunc"} =shift; # ein k-v-Feld fü die Funktion anlegen
return $self; # referenz uf den Hash zurrüliefern.
}
1;
#
package main;
#
$myPackHash=mPack->new("\n ein Scalar\n",
[" eine "," neue "," Liste "],
{"key1"=>"val1","key2"=>"val2"},
sub {print "mypackHash ...eine memberfunktion "}
);
print "\n-------------------------------------------------------\n";
print $myPackHash->{"myScalar"}; print "\n";
print @{$myPackHash->{"myList"}}; print "\n";
print %{$myPackHash->{"myHash"}}; print "\n";
&{$myPackHash->{"myFunc"}};
print "\n-------------------------------------------------------\n";
#
#
Anhänger eines "guten objektorientierten Programmierstiles"
werden sich vielleicht nicht mit der Tatsache anfreunden können, dass
ein Anwendungsprogrammierer über <hashname>->{<feldname>}
auf alle Felder der Datenstruktur zugreifen kannn. In anderen OO-Sprachen
werden Felder die nicht vom Anwendungsprogrammierer manipuliert werden
sollen als "privat" gekennzeichnet.
PERL geht hier einen grundsätzlich anderen Weg. Alle Felder einer solchen Struktur liegen für den Anwendungsprogrammierer offen, und es liegt bei der Vernunft des Anwendungsprogrammierers von der "verpönten" Zugriffsmethode Gebrauch zu machen oder auch nicht.
Larry Wall beschreibt die Sicherheitsphilosophie eines PERL-Modules so:
It would prefer if you stay out of it's livingroom, just because you weren't invited, not because it has a shootgun...
Die Tatsache, dass dem Anwendungsentwickler alle Türen offenstehen, ist also ein FEATURE, kein unbeabsichtigter DESIGNFEHLER. Hier mag auch die Tatsache eine Rolle spielen, dass, anders als in rein kommerziellen Sprachen, ein Anwendungsentwickler vollen Zugriff auf den Quellcode der Module hat.
In der OOP wird der Zugriff auf Datenfelder eines Objektes meist über eigene, an das Objekt gebundene
Routinen, sogenannte Memberfunctions, durchgeführt. Dabei wird auf den Wert eines Feldes
eines Objektes (im Beispiel das Feld myName" ) über eine Funktion des Objektes
(im Beispiel "getMyName()"), zugegriffen. Dies hat gegeüber dem direkten Zugriff
den Vorteil, dass es nicht zu versehentlichen Manipulationen am Feld selbst kommt.
Die Manipulation des Feldes erfolgt ebenfalls über eine Memberfunction, im Beispiel die Routine
(setMyName). Da die "setXXX" Routine prototypisiert ist, wird ein unbeabsichtigtes Initialisieren
mit einem falschen Typ vermieden.
Bevor ein Objekt jedoch auf eigene Felder zugreifen kann, muss das Objekt auf sich selbst zugreifen können, d.h: es muss eine Referenz auf das Objekt selber vorhanden sein. Mit dem Aufruf einer Memberfunction in der Form:
object->funktion(...)
Wird diese Selbstreferenz als 0-ter Parameter ($_[0]) übergeben, erst optionale weitere
Elemente der Parameterliste @_ enthalten die eigentlichen Parameter der Memberfunktion:
package namedObject;
#
sub getName() {
my $self=shift;
return $self->{myNAME};
}
#
sub setName ($){
$self=shift;
my $newName=$_[0];
$self->{myNAME}=$newName;
}
#
sub new($) {
my $class = shift;
my $self = {};
$self->{myNAME} = shift;
return bless($self,$class);
}
1;
#
package main;
print "\n\nObjekt mit Namen Hedwig erzeugen :\n";
$myObject=namedObject->new("Hedwig");
print "\nObjektnamen testen: \n";
print $myObject->getName();
print "\nObjektnamen aendern in \"Helga\"... ";
$myObject->setName("Helga");
print "\nneuen Objektnamen testen: \n";
print $myObject->getName();
print "\n\n\n";
#
#
#
Im Funktionsrumpf von getName wird zuerst mit der Anweisung $self=shift;
der Wert von $_[0] abgefragt, und in einem Feld gespeichert, das von da ab eine Referenz
auf den Hash selbst bereitstellt.
Dieses Vorgehen ist ein mühsames manuelles Nachbauen der eingebauten komfortablen
this/self Selbstreferenzen anderer Programmiersprachen,
macht aber den anschließenden Zugriff auf Felder des Hashes durch den Hash "selbst"
über die Selbstreferenz möglich:
return(self->{myName});
Typisch für Memberfunktionen ist, der Aufruf der Memberfunktion aus dem Hauptprogramm heraus:
$myObject->getName();
Routinen die innerhalb eines Packages Objekte erzeugen, heißen im OOP-Slang
Konstruktoren. Die Routine new im obigen Beispiel ist so ein
Konstruktor. In diesem Konstruktor wird ein Hash erzeugt und zurückgeliefert.
Wodurch unterscheidet sich aber ein Objekt von ganz einem gewöhnlichen Hash?
Sehr informell kann man die Antwort so formulieren:
Der Unterschied besteht darin, dass ein Objekt Informationen über die eigene Klasse besitzt.
Auf PERL-Verhältnisse übertragen kann man dies sogar noch etwas bescheidener formulieren:
Ein Objekt ist ein Hash, der weiß zu welchem Package es gehört.
Was bedeutet es aber konkret für einen Hash "zu wissen zu welchem Package er gehört"? Ohne allzusehr auf
Wie schon erwähnt wird die Symboltabelle eines Paketes selber in einem Hash gehalten, und
der Name dieses Hashes besteht aus dem Namen des Paketes, an den noch zwei Doppelpunkte angehängt werden.
Ein Objekt das "weiss zu welcher Klasse es gehört"
ist also nichts weiter als ein Hash der eine Referenz auf den "Symboltabellenhash"
seines Packages besitzt. Diese Referenz wird implizit mit der eingebauten Funktion
bless angelegt. Die Anweisung:
bless($object,$class);
sorgt für die Verbindung von Objekt $object und Klasse $class.
Im Folgenden bezeichnen wir diesen relativ trivialen Vorgang OOP-Speech-konform etwas hochtrabend als das "Binden eines Objektes an eine Klasse".
Die Funktion bless kann mit einem oder zwei Parametern aufgerufen werden:
bless $REF $CLASSNAME
bless $REF
Wird beim Aufruf nur der Parameter (REF)angegeben, dann wird das Objekt
an das aktuelle Package gebunden.
Im
Abschnitt über Vererbung werden wir aber noch sehen, das es meistens
sinnvoll ist die zweiparametrige Langform zu verwenden.
bless liefert als Ergebnis die Referenz auf das gerade abgesegnete Objekt zurück. Funktionen mit denen neue Objekte erzeugt werden heissen
Konstruktoren . Die Funktion "new" aus dem obigen Beispiel
ist so ein Konstruktor.
new initialisiert einen Hash, bindet ihn an die Klasse namedObject und liefert eine Referenz
zurück, wobei die letzten beiden Aktionen in einer Zeile mit der Anweisung
return bless($self,$class);
erledigt werden.
Anders als in manchen anderen OO-Sprachen ist der Name "new" für Konstruktoren
in keiner Weise zwingend sondern nur Konvention. Eine Klasse kann durchaus
eine Menge von völlig verschiedenartigen Objekten enthalten, wobei für jedes
Objekt ein anderer Konstruktor zuständig ist. Im Folgenden Codeschnipsel benutzen wir
zwei verschieden Konstruktoren (obj1, obj2) um zwei Objekte derselben Klasse aber mit unterschiedlichen
Feldern zu erzeugen. Beide Objekte kennen aber die Methode "printMyPack", weil beide Objekte mittels bless der Klasse myPack zugeordnet wurden:
package myPack;
sub classMethod(){
print "eineKlassenmethode";
}
sub printMyPack($){
$self=shift;
@keylist=keys(%$self);
$keynum=($#keylist+1);
print "\n $keynum Schluessel gefunden \n";
}
sub obj1{
$class=shift;
$self={};
$self->{"key1"}="val1";
return bless($self,$class);
}
sub obj2{
$class=shift;
$self={};
$self->{"key1"}="val1";
$self->{"key2"}="val2";
return bless($self,$class);
}
package main;
$mOBJ1=myPack->obj1();
$mOBJ1->printMyPack;
$mOBJ2=myPack->obj2();
$mOBJ2->printMyPack;
myPack->classMethod();
Die eben aufgezeigt Möglichkeit "vielgestaltige" Objekte derselben Klasse zu erzeugen wird im OO-Slang gerne auch als Polymorphie bezeichnet.
Vergleicht man die Methode classMethod()aus dem vorangegangenen Beispiel mit der Methode
printMyPack(), dann fällt auf, dass die Methode classMethod
keine spezifischen Daten des Objektes benutzt und keine Referenz auf ein Objekt besitzt,
sondern "nur" über allgemeine Informationen über die Klasse verfügt.
Folgerichtig wird classMethod mit der Klasse als nulltem Argument aufgerufen,
während printMyPack() ein Objekt als nulltes Argument bekommt.
Man spricht bei Methoden die ohne Objektreferenz auskommen auch von Klassenmethoden,
bei Methoden die eine Objektreferenz benutzen von Objektmethoden.
In der Sache bieten Klassenmethoden natürlich nichts neues, es sind die altbekannten
Paketspezifischen Subroutinen, lediglich die Aufrufsyntax hat sich von
packagename::funcname() zu packagename->funcname() geändert.
Oft, zum Beispiel bei der Programmierung in JAVA spricht man auch von
statischen Methoden statt von Klassenmethoden.
Eine der meistgeliebten Features einer richtigen OO-Sprache ist die Vererbung. Dabei wird eine neue Klasse (Child) als von einer oder mehreren anderen Klassen Eltern, Parents abgeleitet deklariert. Das Kind erbt von den Eltern (je nach Features der OO-Sprache) alle, oder nur ausgewählte Methoden und Felder.
Während es einige OO-Sprachen (z.B: C++) zulassen, dass ein Kind mehrerer Eltern haben kann ("Mehrfachvererbung","multiple Inheritence"), wird dieses Feature von anderen OO-Sprachen (z.B: JAVA) bewusst nicht unterstützt. Mehrfachvererbung gilt bei vielen OO-Anhängern als "schmutziges Feature", da Mehrfachvererbung leicht zu Namensraumkonflikten führen kann. Bei der einfachen Vererbung stammt jede Klasse nur von einer Reihe von Vorgängern ab. Deklariert eine Klasse eine Methode mit demselben Namen wie eine Vorgängermethode, so überschreibt die "Kindklasse" die Methode des "Ahnen". Bei mehrfacher Vererbung kann jedoch der Fall eintreten, dass eine Klasse zwei gleichnamige Methoden von zwei Ahnen erbt, die nicht untereinander in einem Verwandschaftverhältnis stehen. In diesem Fall entsteht das Problem welche Methode ein Objekt ausführen soll, wenn diese Methode aufgerufen wird. Das Problem wird in vielen OO-Sprachen (z.B: C++) noch dadurch verschärft, dass die einzubindenden Ahnen-Klassen in Form von vorkompilierten proprietären Bibliotheken vorliegen, deren Anbieter einige Features gerne undokumentiert lassen um eigener Anwendungssoftware das Bestehen im harten Wettbewerb zu erleichtern.
Nach dem vorangegangenen Abschnitt dürfte es schon klar sein, dass PERL die Mehrfachvererbung unterstützt.
Das magische Phänomen der Vererbung ist in PERL auf die Verwendung bestimmter Suchpfade reduziert. Dabei wird für eine Klasse eine Liste von anderen Klassen angegeben in denen nach Feldern oder Methoden gesucht wird die in der aktuellen Klasse nicht gefunden werden können. Dadurch erbt die suchende Klasse alle Felder und Methoden der Klassen im Suchpfad.
Der Name der Suchliste ist @ISA, was von "is a" wird. Eine erbende Klasse
ist eine erbende Klasse plus vom Programmierer hinzugefügter Mehrwert.
In der Praxis treten oft Fälle auf, bei denen man nur wenige Zusätze an einer Methode eines Parentobjektes anbringen möchte. In diesem Fall bietet es sich zunächst an, die "Copy and Paste" Funktion seines Editors zu nutzen um den ganzen Code der zu überschreibenden Funktion "neu" zu schreiben, und dann einige kleine Veränderungen anzubringen. Letzteres hat aber den Nachteil, das jede Änderung der ursprünglichen Funktion eine erneute Copy-and-Paste-Aktion nach sich ziehen muss , was Arbeit bedeutet. Glücklicherweise bietet PERL dem auf ökonomische Verwendung der eigenen Arbeitskraft bedachten Programmierer eine Möglichkeit derartige Resssourcenverschwendung zu vermeiden: Mit dem Schlüsselwort SUPER:: kann explizit auf die Methoden der übergeordneten Klasse zugegriffen werden.
Im folgenden Beispiel wird eine Klasse "DreiZaehler" definiert, die die Zahlen von eins bis drei in der richtigen Reihenfolge ausgeben kann. Bei der Implementierung der Klasse "FuenfZaehler" die die Zahlen von eins bis fünf in der richtigen Reihenfolge ausgeben soll, wird mit dem Aufruf
$self->SUPER::zaehlen();
die in der Klasse DreiZaehler vorhandene Intelligenz ausgenutzt, um sich die ersten drei Zahlen in der richtigen Reihenfolge zu verschaffen:
package DreiZaehler;
# Eine Klasse die bis drei zaehen kann
#
sub zaehlen(){
print "1,2,3";
}
sub new(){
$class=shift;
return bless( { } ,$class);
}
package FuenfZaehler;
# Eine Klasse die bis fuenf zaehlen kann
#
@ISA=(DreiZaehler);
sub zaehlen(){
$self=shift;
$self->SUPER::zaehlen();
print ",4,5";
}
package main;
(FuenfZaehler->new())->zaehlen();
Um die vorangegengenen Ausführungen mit etwas mehr Leben zu füllen, betrachten wir im Folgenden ein Beispiel das schon fast aus dem wahren Leben gegriffen sein könnte. Der zugehörige Code is leider etwas umfangreicher und daher komplett an das Ende dieses Abschnittes ausgelagert.
Es sollen automatisch HTML-Seiten mit Warnungen oder Hinweisen erzeugt und auf die Konsole ausgegeben werden. Dabei soll es 3 Klassen von Seiten geben:
Eine HTML-Seite besteht prinzipiell aus zwei Komponenten, dem Kopf (header), der einige Metadaten über den Inhalt der Seite enthält, und dem Körper (body) der den eigentlichen Text enthält. Header und Body werden durch spezielle Formatierungsanweisungen, sogenannte "tags" begrenzt.
Das ganze HTML-Dokument wird von den Tags <html> und </html>
eingeschlossen.
Der Beginn des Bodies wird durch das Tag <head>, das Ende des Bodies
durch </head> markiert. Dazwischen kan sich der Titel befinden, der durch
die Tags <title> und </title> eingeschlossen wird.
Der Beginn des Körpers wird durch das Tag <body>, das Ende des Headers
durch </body> markiert. Dazwischen kan sich eine Überschrift befinden, die durch
die Tags <h1> und </h1> eingeschlossen wird. Außerdem
kann sich noch "normaler" Text im Körper befinden, der ohne spezielle Tags auskommt.
Wenn spezielle Farben für Vorder-- oder Hintergrund gewünscht werden, dann kann der
kann das im Bodytag vermerkt werden, z.B für roten Hintergrund und grünen Vordergrund so:
<body bgcolor=red text=green >
Es bietet sich an, die oben gemachte Typ-Einteilung der HTML-Dokumente in eine Klassenstruktur umzusetzen:
HTMLPage
\
MHTMLPage
\
ColoredHTMLPage
Dabei stellt die Klasse HTMLPage eine einfache Warnung (HTML-Seite mit Überschrift) bereit.
Die Klasse MHTMLPage beerbt diese Basisklasse, erhält als zusätzliche
Eigenschaft noch einen Hinweistext, und die Fähigkeit diesen auszugeben. Die Klasse
ColoredHTMLPage erweitert MHTMLPage nochmal um Vorder und Hintergrundfarben, und
die Fähigkeit diese im body Tag auszugeben.
Die Basisklasse HTMLPage wird mit dem Titel als einzigem Parameter initialisiert.
HTMLPage bietet die Methode printHTMLPage um die HTML-Seite auf
die Konsole auszugeben. printHTMLPage besteht nur aus dem Aufruf von drei anderen
Methoden: printHeader, printBody und printTail, die
den Header, den Body, und die abschlißenden Tags ausgeben. Während printHeader()
und printBody() mit der Anweisung
$self=shift;
eine Referenz auf das aufrufende Objekt anlegen, um so auf den Titel der Seite zugreifen zu können
kommt printTail() ohne eine solche Selbstreferenz aus.
printHeader und printBodysind also Objektmethoden, printTail eine Klassenmethode.
Die Klasse MHTMLPage muß zusätzlich zur Überschrift
im Body noch eine Nachricht (Message) darstellen, während die
Ausgabe von Header und Tail sich gegenüber HTMLPage nicht verändert hat.
Mit der Anweisung:
@ISA = (HTMLPage);
wird dafür gesorgt, dass MHTMLPage ein Nachkomme von HTMLPage ist, so dass
die Methoden printHeader() und printTail einfach von HTMLPage
geerbt werden können.
Neu hinzu kommen das Feld message und die Methode getMessage()
für Speicherung und Zugriff auf die Meldung.
Der Konstruktor new und die Methode printBody() müssen
angepasst werden um die Meldung bei der Initialisierung entgegenzunehmen, bzw.
um die Meldung im Body der Seite auszugeben. In beiden Fällen wird SUPER:: benutzt,
und auf die entsprechenden Methden der übergeordneten Klassen zuzugreifen.
Der Erweiterung von MHTMLPage zu coloredHTMLPage verläuft
im wesentlichen analog zu der oben beschriebenen Erweiterung von
HTMLPage zu MHTMLPage. MHTMLPage wird um die
Felder textcolor und bgcolor für Vorder und Hintergrundfarbe,
sowie die entsprechenden Zugriffsmethoden erweitert. Der Konstruktor new
muss überschrieben werden um die beiden neuen Parameter zu verarbeiten,
wobei mit super::new(...) auf schon implementierte Funktionalität zugegriffen
wird. Lediglich die methode printBody() wird völlig neu geschrieben.
Das Hauptprogramm prüft mit wievielen Parametern das Script aufgerufen wurde. Wird das Script mit nur einem, zweien, oder vier Kommandozeilenparametern aufgerufen, dann wird eine HTML-Seite des ersten, zweiten oder dritten Typs erzeugt, wobei der erste Parameter als Titel, zweiter, dritter, und vierter Parameter, -soweit vorhanden- als Message, Hintergrundfarbe oder Vordergrundfarbe interpretiert werden.
#!/usr/bin/perl -w
#
package HTMLPage;
# Diese Klasse stellt eine einfache
# HTML-Seite bereit, die nur einen Titel, aber
# keine weiteren Daten hat.
# Einen einfachen HTML-Header ausgeben.
sub printHeader(){
my $self=shift;
my $title=$self->getTitle();
print << "EOT";
<html>
<header>
<title> $title </title>
</header>
EOT
}
# einen HTML-Text ausgeben, der nur den Titel als Ueberschrift enthaelt
sub printBody{
my $self=shift;
my $title=$self->getTitle();
print << "EOT";
<body>
<h1>
$title
</h1>
EOT
}
# einen Text ausgeben, der die abschliesenden Tags
# "</body>" und "</html>" enthaelt.
sub printTail(){
print '</body>',"\n";
print '</html>',"\n";
}
# erlaubt den Zugriff auf den Titel des Dokuments
sub getTitle(){
my $self=shift;
return $self->{"title"};
}
# Das ganze Dokument ausgeben.
sub printPage(){
my $self=shift;
$self->printHeader();
$self->printBody();
printTail();
}
# ein neues einfaches HTML-Dokument erzeugen.
sub new($){
my $class=shift;
my $title=shift;
my $self={ };
$self->{"title"}=$title;
return bless $self , $class ;
}
1;
package MHTMLPage;
# Diese Klasse beerbt HTMLPage, und stellt zusaetzlich
# Moeglichkeiten zur Ausgabe von echtem Text
# (Message bereit)
#
@ISA = (HTMLPage);
sub getMessage(){
$self=shift;
return $self->{"message"};
}
# body bestehend aus Titel und Message
sub printBody(){
my $self=shift;
my $title=$self->getTitle();
my $message=$self->getMessage();
$self->SUPER::printBody();
print "\n\n$message\n\n";
}
# neues MHTMLPage Objekt erzeugen,
# mit Titel und Message als initialen Parametern
#
sub new($$){
my $class=shift;
my $title=shift;
my $message=shift;
my $self=$class->SUPER::new($title);
$self->{"message"}=$message;
return $self;
}
1;
package ColoredHTMLPage;
# Diese Klasse ergaenzt die MHTMLPage
# um die Moeglichkeit Vorder und Hintergrundfarbe
# dateiweit setzen zu koennen
#
@ISA=( MHTMLPage );
#
# Zugriff auf die Hintergrundfarbe
#
sub getBgColor(){
$self=shift;
return $self->{"bgcolor"};
}
#
# Zugriff auf die Textfarbe
#
sub getTextColor(){
$self=shift;
return $self->{"textcolor"};
}
# Body-Tag enthaelt Hintergrundfarbe
sub printBody{
my $self=shift;
my $title=$self->getTitle();
my $bgcolor=$self->getBgColor();
my $textcolor=$self->getTextColor();
my $message=$self->getMessage();
print << "EOT";
ContentType text/html "\n"
"\n"
<body bgcolor=$bgcolor text=$textcolor>
<h1>
$title
</h1>
"\n\n $message \n\n"
EOT
}
#
#
#
sub new($$$$){
my $class=shift;
my $title=shift;
my $message=shift;
my $bgcolor=shift;
my $textcolor=shift;
my $self=$class->SUPER::new($title,$message);
$self->{"bgcolor"}=$bgcolor;
$self->{"textcolor"}=$textcolor;
return $self;
}
1;
package main;
$argnum=$#ARGV+1;
if($argnum == 4){
$myPage=ColoredHTMLPage->new($ARGV[0],$ARGV[1],$ARGV[2],$ARGV[3]);
$myPage->printPage();
exit 0;
}
if($argnum==2){
$myPage=MHTMLPage->new($ARGV[0],$ARGV[1]);
$myPage->printPage();
exit 0;
}
if($argnum==1){
$myPage=HTMLPage->new($ARGV[0]);
$myPage->printPage();
return 0;
}
exit 1;
Im vorangegangenen Beispiel wurden ( mit MHTMLPage->new(...) und
ColoredHTMLPage->new(..)) zwei Konstruktoren behandelt, die ohne den
expliziten Aufruf von bless auskamen. Trotzdem wurden die dabei erzeugten Objekte
korrekt an die entsprechenden Klassen gebunden, weil der Aufruf von
bless mittels SUPER::new() im Konstruktor der
übergeordnete Klasse erfolgte, und die Klasse als $class
explizit an die bless Funktion übergeben wurde.
Der Aufruf von Konstruktoren anderer
Klassen mittels SUPER:: oder durch einfache Vererbung ist aber nicht
ohne Tücken. Im folgenden Beispiel wird eine Basisklasse Base
definiert und mit zwei Konstruktoren ausgestattet. Der erste Konstruktor erzeugt einen
leeren Hash und bindet ihn mit der Kurzform bless($) an die aktuelle
Klasse. Der zweite Konstruktor verschafft sich mit $class=shiftexplizit
die aktuelle Klasse, erzeugt dann einen leeren Hash und bindet ihn mit der Langform
von bless($$) an die Klasse $class:
package Base;
sub printClass{ print "Base"; }
sub constr1{
return bless ({});
}
sub constr2 {
$class=shift;
return bless({},$class);
}
1;
package Derived;
@ISA = (Base);
sub printClass{ print "Derived"; }
1;
package main;
$myObj1=Derived->constr1( );
print "\n\n Klasse von myObj1 : ";
$myObj1->printClass();
$myObj2=Derived->constr2( );
print "\n\n Klasse von myObj2 : ";
$myObj2->printClass();
Das Programm erzeugt zwei Objekte, die Auskunft über ihre Klasse geben:
Klasse von myObj1 : Base
Klasse von myObj2 : Derived
Das Objekt Nr 2, das mit der Langform der bless Funktion an seine Klasse gebunden
wurde gibt -wie erwartet- "Derived" als Name seiner Klasse aus. Das Objekt Nr1, das mit der
Kurzform gebunden wurde ist vom Typ Base, da beim Binden des Objektes mit der Kurzform
die Klasse auf das package gesetzt wurde in dem bless aufgerufen wurde.
Konstruktoren die vererbt werden sollen sollten
immer die Langform der bless-Funktion benutzen .
Da man selten voraussagen kann, was mit eigenem Code noch alles passieren wird, ist es
vielleicht sogar besser generell auf die Verwendung der kurzen Variante von bless
zu verzichten.