Objektorientierung OOP und Web Application Framework

Der Sinn einer flachen Klassenhierarchie und die Frage der Zweckmäßigkeit von OOP

Wer objektorientiert entwickelt, wird sich recht bald immer wieder die Frage stellen: Wie gestalte ich den Datenaustausch von Klasse zu Klasse bzw. zwischen Instanzen verschiedener Klassen. Und immer wieder ist die Antwort dieselbe, was das Herstellen solcher Beziehungen/Verbindungen betrifft, denn es gibt genau zwei Möglichkeiten: Vererbung oder Delegation. Natürlich ist es denkbar, alle möglichen Teilaufgaben, vom Eingang eines HTTP-Request bis zur Auslieferung der Response, in Klassen aufzuteilen, Instanzen zu erstellen und deren Methoden auszuführen. Es ist jedoch die Frage, inwieweit das immer sinnvoll und vor Allem so zu machen ist, dass am Ende die Übersicht nicht verloren geht.

Request und Response

Vom Allgemeinen zum Besonderen stellt sich zunächst die Frage, ob für Request und Response ein gemeinsames Objekt oder zwei verschiedene Instanzen der jeweiligen Klasse zuständig sein sollen. Das hier vorliegende Framework löst diese Frage wie folgt: Das für die Auslieferung der Response zuständige Objekt ist eine Instanz an die an den URL gebundene Klasse und wird über ein als eigenes Attribut eingebautes Objekt dazu befähigt, Daten/Parameter eines Request verarbeiten zu können. Im Konstruktor der eigenen Response-Klasse sieht das beispielsweise so aus:

    use xCGI;
    sub new{
        my $class = shift;
            # Name der Response-Klasse
        bless{
            CGI => xCGI->new();
        }, $class;
    }

Und die param()-Methode wird ganz einfach delegiert, d.h., sie wird namentlich zu einer eigenen Methode gemacht:

    sub param{
        my $self = shift;
        return $self->{CGI}->param(@_);
    }

Im Ergebnis dieser Delegierung ist die Klasse xCGI austauschbar, vorausgesetzt, der Ersatz bringt namentlich dieselbe Methode param() mit, womit Parameter im Request geparst werden können. Zweckmäßig ist es außerdem, bei jedem Request zunächst erst einmal festzustellen, ob dieser überhaupt Parameter mitbringt. Über diese einfache Prüfung wird im Framework der zum URL gehörige Controller aufgerufen, der nichts weiter ist als eine Methode der Response-Instanz:

    # $ro ist das Response-Objekt
    if( $ro->param() ){ $ro->browse() }
    else{ $ro->control() }

Der Speicherort für das Template

Jede, vom Content-Type text/html erzeugte Response bekommt per Konfiguration neben dem Namen der an den URL gebundenen Klasse eine Reihe von Eigenschaften zugewiesen. Je nach Klasse kann eine dieser Eigenschaften die Pfadangabe zur Template-Datei beinhalten, sofern das also in de Klasse so vorgesehen ist. Templates gibt es jeweils für den <head>-Bereich, den <footer> und den <body> sowie den Navigationsbereich einer jeden HTML-Response. Dieser Aufteilung entsprechend verfügt das Response-Objekt über Methoden, die es zur Erzeugung bzw. Ausgabe dieser HTML-Bereiche befähigen.

Über eine einheitliche Methode eav() hat das Response-Objekt Vollzugriff auf die für einen URL konfigurierten Eigenschaften, z.B. $self->eav('title', 'Neuer Titel ...'); und da das Template für den <body> als Instanz-eigene Eigenschaft gepuffert wird, kann es bei Bedarf komplett ausgetauscht werden: Zum Beispiel dann, wenn sich aufgrund von Benutzereingaben eine andere Sicht ergibt. Des Weiteren werden alle Daten, die später in das Template zu rendern sind, in einer Instanz-eigenen Eigenschaft gepuffert. Die letzte Möglichkeit, diesen sogenannten STASH noch vor der Ausgabe der Response zu ändern, ergibt sich in der Framework-Interface-Methode trailer().

Anbindung MySQL u.a. Datenquellen

Die Schnittstelle zu MySQL u.a. Datenquellen ist in Perl über eine dedizierte Klasse implementiert. D.h., für den Zugriff auf MySQL ist in jedem Fall ein DataBaseHandler (DBH) über die Perl-Klasse DBI zu erstellen. Ohne diesen DBH im eigenen Objekt als Eigenschaft zu haben (nicht jede Response-Seite braucht MySQL) wird dieser auch bei Verwendung nicht sichtbar, sondern verbleibt in einer über die Factory ausgelagerten Methode. Letzere wird nur bei Bedarf aus dem Dateisystem geladen und zur Laufzeit kompiliert. Auch die Methode zur Erstellung eines DBH wird im Rahmen der Factory ausgelagert, sie bekommt als Argument lediglich den Namen der Datenbank übergeben: $self->{DBH} = $self->dbh('webdaten');

Daten für die Session

Sessiondaten sind über eine eigene Klasse als Hash an eine Datei gebunden. Um auf diesen Hash zugreifen zu können, ist dieser als Eigenschaft der eigenen Instanz verfügbar:

    tie %SESSION, 'SessionFile', file => $sid or die $@;
    $self->{SESSION} = \%SESSION;
    tied({$self->{SESSION}})->write();
        # Änderungen persitent machen

Fazit OOP und Web Application Framework

Ohne OOP ist es nicht möglich, ein Framework wie hier vorliegend aufzubauen. Sämtliche, jeweils an einen URL gebundene Klassen erben von Class main und hier liegt das eigentliche Framework, was eine Handvoll Interface-Methoden definiert, die stets in immer wiederkehrender Reihenfolge abgearbeitet werden sobald ein URL requested wird. Es ergeben sich Verzweigungen in diesem Ablauf, wenn Parameter im Request sind.

Für den Datenaustausch erforderliche Beziehungen zu anderen, nicht verwandten Klassen werden über den Einbau derer Instanzen als Eigenschften der eigenen Klasse hergestellt wie z.B. die Eigenschaft {CGI} als Instanz einer fremden Klasse CGI. Schnittstellen zu weiteren beliebigen Datenquellen werden als Methoden im Rahmen einer Factory implementiert. Auf diese Art und Weise ist das Response-Objekt als Instanz der an einen URL gebundenen Subklasse in der Lage, Request-Parameter zu verarbeiten, Daten für die Response, einschließlich auszugebender HTTP-Response-Header zu puffern und die Response gleich welchem Content-Type an den Webserver auszuliefern.


Anbieter: nmq​rstx-18­@yahoo.de, die Seite verwendet funktionsbedingt einen Session-Cookie und ist Bestandteil meines nach modernen Aspekten in Perl entwickelten Frameworks.