Ankündigung

Einklappen
Keine Ankündigung bisher.

Strukturierung größerrer Projekte

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Strukturierung größerrer Projekte

    Guten Morgen!
    Ich habe leider immer öfter das Problem, dass ich nicht weiß, wie ich größere Projekte Strukturieren soll. Daher wende ich mich nun hoffnungsvoll an euch.

    Eine meiner Meinung nach sinnvolle Struktur habe ich mal in dieser PDF-Datei aufgezeigt.

    Nun habe ich allerdings das Problem, dass ich nicht weiß, wie ich die einzelnen Module sinnvoll aufbauen soll. Eins ist sicher jedes Modul, ich nehme jetzt mal als Beispiel ein News-Modul, soll sein eigenes Template sowohl für das Backend, als auch für das Frontend besitzen. Die einzelnen Aktionen, die innerhalb eines Moduls verfügbar seien sollen, sollen ausgelagert und am besten auch getrennt werden. Doch, wie lagere ich die Aktionen am geschicktesten in eine oder mehrere Dateien aus und wie führe ich letzt endlich alles am geschicktesten zusammen? Und wie mache ich am geschicktesten Objekte, wie eine Datenbank-Verbindung oder geladenene Konfigurationen global verfügbar?

    Viele Fragen und gleich das nächste Problem? Wie kann ich mir Ausgaben erleichtern? Gibt es da einfach Muster, wie zum Beispiel bei einer Pager-Klasse, die eine Blätterfunktion ersetzt? Die Ausgabe von Daten in sortierbaren Tabellen ist sehr schick und für den User absolut angenehm, aber, wenn man sie jedesmal in das Layout einpassen muss, doch sehr ermüdent und zeitaufwendig. Lohnen sich hier Klassen, die bei solchen Aufgaben helfen und welche lassen könnten das neben einer Pager-Klasse noch sein?

    Und gleich die nächste Frage. Natürlich ist es nicht schön, wenn man einmal etwas gecodet hat und es beim nächsten mal wieder schreiben muss, da man nicht alles wiederverwenden kann. Daher möchte ich gerne ein Grundsystem schreiben, dass eigentlich nicht mehr erlaubt als Benutzer, Einstellungen und Packages (Module) zu verwalten. Wie könnte so eine Verwaltung aussehen? Ich weiß es leider nicht. Ist es sinnvoll auf einfache Tarballs zurück zu greifen und sollte ich diese lieber einfach auf dem komplizierten Weg über die file()-Funktionen entpacken oder sollte ich die Gefahr auf mich nehmen, dass auf dem einen oder anderen Webspace exec() und system() verboten bzw. gesperrt sind? Wie könnte die Package-erwaltung weiter aussehen? Sollte eine Klasse die Installation eines Packages übernehmen? Das könnte zum Beispiel dann ja einfach so aussehen, dass diese Klasse einen Tarball einfach in ein temporäres Verzeichnis entpackt und dort eine install.php aufruft, die alle wichtigen Dateien verschiebt, Datenbanken erstellt und erste Einträge vornimmt. Ein Problem wäre dann die Deinstallation dieses Paketes. Eine weitere die Verknüpfung zwischen Paketen. Wie kann man überprüfen, ob ein Paket überhaupt gelöscht werden darf ohne andere zu beeinflussen? Sollten installierte Pakete in einer Datenbank erfasst werden?

    Wie schon gesagt - sehr sehr viele Fragen, aber ich hoffe ihr könnte mir etwas weiter helfen.
    MfG, Andy

  • #2
    Hallo Andy,

    du formulierst viele Fragen, auf die ich gerne eingehen will. Allerdings ist jetzt schon recht spät und ich habe morgen eine lange Fahrt inkl. Hochzeitseinladung vor mir. Ich melde mich deshalb am Sonntag mit einer ausführlichen Antwort.

    Zu deinem Post habe ich folgende schnelle Hinweise:
    - Basis für Entwicklung: Adventure PHP Framework - Startseite
    - Strukturierung von Modulen: Adventure PHP Framework - Gaestebuch Tutorial
    - Pager: Adventure PHP Framework - Mitgelieferte Module
    - Datenbank-Abstraktion: Adventure PHP Framework - Klassenreferenz connectionManager
    - Wiederverwendbarkeit: Adventure PHP Framework - Kommentar Funktion Tutorial
    - Grundsystem User-Verwaltung: Adventure PHP Framework - Generischer OR Mapper und forum.adventure-php-framework.org [de] • Thema anzeigen - GenericORMapper

    Ich hoffe da ist etwas für dich dabei.
    Viele Grüße,
    Dr.E.

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1. Think about software design [B]before[/B] you start to write code!
    2. Discuss and review it together with [B]experts[/B]!
    3. Choose [B]good[/B] tools (-> [URL="http://adventure-php-framework.org/Seite/088-Why-APF"]Adventure PHP Framework (APF)[/URL][URL="http://adventure-php-framework.org"][/URL])!
    4. Write [I][B]clean and reusable[/B][/I] software only!
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Kommentar


    • #3
      Hallo Andy,

      um ein System möglichst modular zu entwickeln, benötigt man eine PAC-Ähnliche Architektur.
      Um möglichst flexibel zu bleiben sollte aber jedes Modul die Möglichkeit besitzen, eigenständige
      Models (Daten), Views (Präsentation) und Controller (Logik) zu haben.
      Somit kann ein Modul zum Beispiel Frontend Views & Controller, und Backend Views & Controller
      haben die auf das gleiche Model zurückgreifen. Die Module bleiben damit voneinander unabhängig.
      Jetzt kommt natürlich die Frage auf, wie man die Module am besten zusammensteckt.
      Einerseits sollen die Module unabhängig sein, andererseits benötigt man Flexibilität, damit sie
      miteinander interagieren können. Zusätzlich muss der letzte Status jedes Moduls gespeichert werden.

      Beispiel:
      Das Menü und der Inhaltsbereich einer Internetseite sollen als getrennte Module entwickelt werden.
      Das Menü benötigt immer den letzten Status um zu wissen, welcher Knoten gerade aufgeklappt ist,
      schließlich soll es ja nicht einfach wieder zuklappen wenn jemand im Inhaltsbereich auf "weiter lesen" klickt.
      Andererseits soll durch eine Navigation im Menü nicht direkt eine andere Seite im Inhaltsbereich erscheinen -
      jedenfalls nicht wenn man nur eine Kategorie im Menü aufklappt.
      Um die Usabilty weiter zu verbessern, soll im Menü immer der aktuelle Bereich aufklappen, der im
      Inhaltsbereich ausgewählt wurde. Gelangt ein User also durch einen Klick auf einen Link in einen
      anderen Bereich der Seite soll sich das Menü dazu entsprechend verhalten.

      Damit haben wir also 2 Module die miteinander agieren. Beide müssen ihren letzten Zustand wissen
      - ergo speichern - und über neue Events benachrichtigt werden. Das Menü hat die Events auf/zu klappen
      und der Inhaltsbereich das Event anzeigen. Der Status eines Moduls kann entweder im Model oder
      über eine Session-Objekt festgehalten werden. Die Benachrichtigung über neue Aktionen geschieht
      üblicherweise über die URL. Bleibt noch die Frage bestehen, wie man die Module einer Applikation
      am besten zusammensteckt. Dazu bietet sich das DOM (Document Object Model) an.
      Es erlaubt eine beliebige Knotenstruktur und ermöglicht es über einfache Methoden, neue Knoten an
      einer beliebigen Stelle einzufügen. Glücklicherweise ist es in PHP bereits implementiert, daher kann man Zeit bei der Entwicklung und Zeit bei der Code-Ausführung sparen.
      (integrierte Klassen sind in der Ausführung immer schneller als eigene)

      Fortsetzung folgt, ich muss zu einem Termin ...
      [FONT="Book Antiqua"][I]"Nobody is as smart as everybody" - Kevin Kelly[/I]
      — The best things in life aren't things[/FONT]

      Kommentar


      • #4
        Vielen dank für die Links und den bisher sehr sehr informativen Post! Muss noch etwas weiterlesen und verarbeiten.
        MfG, Andy

        Kommentar


        • #5
          Hola señor, da bin ich wieder.

          DOM.
          Mit dem DOM kannst du nun also deine Module - beliebig tief verschachtelt - in deinen Applikationsbaum
          einbinden. Jetzt stellt sich die Frage wie du möglichst einfach den voneinander unabhängigen Modulen
          mitteilst auf welche Events sie anspringen sollen. Am einfachsten wäre es den Modulen direkt beim
          Einbinden in den Applikationsbaum einen URL Parameter und ggf. einen Startwert mitzugeben.
          Da kommen wir zum Thema Templates. Es wäre ja eigentlich super, wenn das Hinzufügen der
          Module zum Applikationsbaum über die Templates passieren könnte.
          HTML Dokumente werden ja auch über das DOM repräsentiert. Also könnte man ein Root-Template
          basteln von dem ausgehend alle Module eingebunden werden. Die Module können in ihren
          jeweiligen Templates natürlich auch wieder andere (Teil-)Module einbinden.
          Wie nun die Module im HTML einbinden? Um nicht HTML- und PHP-Code zu vermischen benötigt
          man einen Tag. Zum Beispiel <import namespace="modules::menu:res" module="Menu" urlparam="$MenuAction" connectto="Content" init="StartValue" />
          Damit wäre das Modul "Menü" aus dem Namespace modules:menu:res eingebunden und
          mit dem Modul Content verbunden. Es weiß auf welchen Parameter es für Events hören muss
          und mit welchem Startwert es initialisiert wird.
          So far so good.
          Die Template Komponente des Systems übersetzt die Tags entsprechend und fügt anstelle des
          Tags das jeweilige Module in den Applikationsbaum ein. Jetzt benötigt man nur noch einen
          URL-Manager, der sich darum kümmert die Links des Menüs an den URL-Parameter des
          Content-Moduls zu binden. Um nicht so theoretisch zu bleiben etwas Code:
          Code:
          <html>
            <head>
              [..]
            <body>
              <import namespace="modules::menu::pres" module="Menu" urlparam="$MenuAction" connectto="Content" init="StartValue" />
              [..]
              <import namespace="modules::content::pres" module="Content" urlparam="$ContentAction" init="Home" />
            </body>
          </html>
          Der URL-Manager müsste also erkennen, dass das Menü- mit dem Content-Modul verbunden ist.
          Der Inhalt des Menü-Moduls:
          Code:
          <ul>
            <li><a href="/Start" title="Startseite">Home</a></li>
            <li><a href="/News" title="Aktuelles">News</a></li>
          </ul>
          Der URL-Manager macht daraus:
          Code:
          <ul>
            <li><a href="?ContentAction=/Start" title="Startseite">Home</a></li>
            <li><a href="?ContentAction=/News" title="Aktuelles">News</a></li>
          </ul>
          Das wars auch schon. Die Sache mit den Tags in den Templates kann man nun noch beliebig
          fortführen indem man eine Tag-Library implementiert usw.
          Wenn du den Links zum Adventure PHP Framework gefolgt bist wirst du eigentlich alles hier aufgeführte zumindest in ähnlicher Form wieder finden.
          Das APF ist allerdings wesentlich weitsichtiger angelegt, so kann man dort verschiedene Designs erstellen
          und per loadDesign für Unterseiten ein komplett anderes Design anzeigen und viele andere tolle Sachen über die TagLib machen.

          Frage: Warum schreibt hier nicht noch jemand über seine Erfahrung? Gibt doch so viele Cracks hier
          Wie sieht eure Architektur aus?
          *bray to the ÜMOD*

          PS: Hab dir mal einiges vorweggenommen, dr.e.
          [FONT="Book Antiqua"][I]"Nobody is as smart as everybody" - Kevin Kelly[/I]
          — The best things in life aren't things[/FONT]

          Kommentar


          • #6
            Wenns sein muß, Dummy...

            Ich bin nicht so bewandert in Softwaredesign wie Doc. E. und andere, deshalb werden einige meinen Stil vielleicht als inkonsequent oder unkonventionell bezeichnen. Nur ganz kurz mein derzeitiger Ansatz.

            Ich benutze zu allererst einen Frontcontroller, um meinen Anfrage und Rückgaben zu zentralisieren. Der Frontcontroller verwaltet anfallende Request- und sonstige Parameterdaten (e.g. Sessions). Die Ausgabe - genauer der Response - ist an eine globale GUI Klasse gekoppelt, an die Teilausgaben bestimmter Templatebestandteile gehen. Dazu gleich mehr.

            1. Grundlegende Anwendungsteile stehen in Libraries zur Verfügung, im allgemeinen benutze ich oft das Abstract Factory (AF) Pattern, stelle dann einige Defaultkomponenten zur Verfügung und überlasse es später dem "Entwickler" in seiner Umsetzung, die Codebasis durch weitere Ableitungen und zugehörige konkrete Fabriken zu ergänzen. In passenden Fällen benutze ich ein Verfahren, bei dem sich eine Fabrik bei dem AF-Client als Fabrik anmeldet. Jede so verwaltete Fabrik stellt eine canHandle () Methode bereit. Im Objekt-Erzeugungs-Kontext durchläuft der Client alle eingetragenen Factories mit bestimmten Parametern. Die erste passende Factory erhält den Zuschlag und erzeugt das Objekt. - Wie auch immer man dieses Pattern beziechnen mag, es ist praktisch.

            2. Um eine Modularität zu gewährleisten, teile ich meine Anwendung in - ich nenne es - Komponenten auf. Jede Komponente besitzt einen Controller, der ihr Verhalten steuert und einen, der ihre Ausgabe kontrolliert. Beide Controller werden über Requestparameter gesteuert und melden sich dazu bei der Frontcontrollerkomponente an. Zum Zeitpunkt des Aufrufs der Komponente beziehen sie dann die für sie relevanten Controlling-Daten und reagieren nach vorgegebenem Verhalten. (Letzteres ist momentan noch recht fest verdrahtet)
            Komponenten werden in meiner Anwendung zunächst bekannt gemacht (dabei werden die Klassen eingebunden). Die Businesslogik kann je nach Bedarf anschließend explizit aufgerufen werden oder erst aus dem "Template" kurz vor der Ausgabe (also dem Aufruf der View-Logik).
            Kurz zu den "Templates": Ich benutze nur einfache <?php echo $Inhalt ?> Syntax, weil ich kein Fan von String- oder gar regulären Ersetzungen in diesem Zusammenhang bin. *)
            Aus einem Main-Template werden dann alle Module die eine Ausgabe erzeugen über die GUI Klasse aufgerufen (u.U. auch rekursiv), abgearbeitet und gerendert. Zunächst in einen Puffer, der später in den Response des Frontcontrollers eingeht und dort schließlich nach nötigen Headerausgaben zur Ausgabe kommt.

            3. Um jetzt nicht alle Komponenten, die in der Anwendung vorkommen können, einbinden zu müssen
            - ach ja, Exkurs: ich bin kein Freund von dynamischer Einbindung von Programmteilen über irgendwelche zusammengefrickelten Pfade aus Namespaces und Klassennamen, bei mir ist alles explizit eingebunden. Ich gebe zu, meine Anwendungen waren bisher keinen Schwerlasttests ausgesetzt -
            ... habe ich eine weitere Hierarchiestruktur implementiert: Eine Art Pagecontrolling, das in etwa die thematische Struktur einer Website repräsentiert. Also bspw. eine Page pro Formular und Verarbeitung, wahlweise je eine oder eine gruppierende Seite für Inhaltsseiten etc.
            Auch hier kommt ein Controller zum Einsatz, die Settings sind in einer Datenbank konfigurierbar, dabei können Platzhalter mehrere Thementeile auf eine gemeinsame Page bündeln.

            4. Für diverse Einstellungen und Strukturen, statische Templates und Queryergebnisse kommt Caching zum Einsatz.

            *) Möglicherweise werde ich diese Einstellung zumindest für die Aufrufe der Komponenten überdenken müssen, da bisher keine Trennung der anfallenden Ausgaben möglich ist.
            [COLOR="#F5F5FF"]--[/COLOR]
            [COLOR="Gray"][SIZE="6"][FONT="Georgia"][B]^^ O.O[/B][/FONT] [/SIZE]
            „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
            [URL="http://www.php.de/javascript-ajax-und-mehr/107400-draggable-sorttable-setattribute.html#post788799"][B]Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“[/B][/URL][/COLOR]
            [COLOR="#F5F5FF"]
            --[/COLOR]

            Kommentar


            • #7
              Danke für eure sehr informativen Posts. Ich muss leider zugeben, dass ich den zweiten etwas besser verstanden habe. Ich setzte zudem in Templates auch lieber auf die Templateeigenschaften von PHP und nutze manchmal auch die Alternativsyntaxfür Kontrollstrukturen, wobei ich eigentlich versuche dies zu vermeiden. Leider habe ich nicht richtig verstanden, wie ich mir einen Frontcontroller vorstellen muss. Ist das soetwas wie eine HTTP-Klasse, die eben die verschiedenen Request (HEAD/GET/POST) an ein Script sendet und dabei Cookies, File-Handles und Parameter übergibt, sodass nur die Antwort aufgefangen, zwischengespeichert und dann in ein Template oder Ähnliches eingebunden werden muss? Würde das gnaze ja sehr vereinfachen. Wäre nett, wenn mir das noch jemand etwas genauer erläutern könnte. Zudem würde mich, wie schon gesagt, interessieren, welche sinnvollen Möglichkeiten es gibt, mit OOP Ausgaben zu erleichtern (Beispiel Pager-Klasse). Macht es zum Beispiel Sinn eine Klasse zu schreiben, die eine einfach tabellarische ausgabe anhand eines Arrays macht. Oder ist es irgendwie möglich die Validierung von Formular-Daten zu zentralisieren, sodass nicht mehr so ewig lange Kontroll-Strukturen nötig sind. Habe natürlich noch viele Fragen und diese Werden sich bestimmt noch weiter häufen, wenn ich die Artikel von Dr. e endlich alle durch habe, aber nun muss ich leider auch erstmal wieder weg. Ich möchte mich aber nochmal für eure Hilfe und Mühen bedanken. Danke.
              MfG, Andy

              Kommentar


              • #8
                Front-Controller:
                Ein Front-Controller ist ein Objekt, das alles Anfragen an eine Webapplikation entgegennimmt und die
                Arbeiten durchführt, die bei allen Anfragen identisch sind. Zur Erzeugung der Antwort auf die Anfrage
                leitet es die Anfragen an die Objekte weiter, die die unterschiedlichen Anfragen verarbeiten können.

                (Stephan Schmidt in PHP Design Patterns, O'Reilly)

                Der Front-Controller ist die Zentrale Schnittstelle einer Applikation.
                Um nicht nur auf normale HTTP-Anfragen sondern um zum Beispiel auch auf SOAP oder AJAX, ohne
                die eigentliche Anwendung umschreiben zu müssen, reagieren zu können, abstrahiert man sowohl
                Anfrage als auch Antwort und packt sie in eigene Objekte. Für HTTP wären das HttpRequest
                und HttpResponse, für SOAP dann SoapRequest und SoapResponse und so weiter.

                Da man immer gegen Schnittstellen programmieren sollte, zunächst mal die Interfaces für Request und Response:
                PHP-Code:
                interface Request {
                    public function 
                issetParameter($name);
                    public function 
                getParameter($name);
                    public function 
                getParameterNames();
                    public function 
                getHeader($name);
                }

                interface 
                Response {
                    public function 
                write($data);
                    public function 
                addHeader($name$value);
                    public function 
                setStatus($status);
                    public function 
                flush();

                Konkrete Implementierung (HTTP).
                Request
                PHP-Code:
                <?php
                require_once 'Request.php'// interface

                class HttpRequest implements Request {

                    private 
                $parameters;

                    public function 
                __construct() {
                        
                $this->parameters $_REQUEST;
                    }

                    public function 
                issetParameter($name) {
                        return isset(
                $this->parameters[$name]);
                    }

                    public function 
                getParameter($name) {
                        if (isset(
                $this->parameters[$name])) {
                            return 
                $this->parameters[$name];
                        }
                        return 
                null;
                    }

                    public function 
                getParameterNames() {
                        return 
                array_keys($this->parameters);
                    }

                    public function 
                getHeader($name) {
                        
                $name 'HTTP_' strtoupper(str_replace('-''_'$name));
                        if (isset(
                $_SERVER[$name])) {
                            return 
                $_SERVER[$name];
                        }
                        return 
                null;
                    }
                }
                ?>
                und Response
                PHP-Code:
                <?php
                require_once 'Response.php'// interface

                class HttpResponse implements Response {
                    private 
                $status '200 OK';
                    private 
                $headers = array();
                    private 
                $body null;

                    public function 
                setStatus($status) {
                        
                $this->status $status;
                    }

                    public function 
                addHeader($name$value) {
                        
                $this->headers[$name] = $value;
                    }

                    public function 
                write($data) {
                        
                $this->body .= $data;
                    }

                    public function 
                flush() {
                        
                header("HTTP/1.0 {$this->status}");
                        foreach (
                $this->headers as $name => $value) {
                            
                header("{$name}{$value}");
                        }
                        print 
                $this->body;
                        
                $this->headers = array();
                        
                $this->body null;
                    }
                }
                ?>
                In den Response schreibt man über die write($data) Methode. Sobald die Antwort gesendet werden soll, wird die flush() Methode verwendet.

                Beispiel zur Anwendung:
                PHP-Code:
                <?php
                $request 
                = new HttpRequest();
                $response = new HttpResponse();

                if (
                $request->issetParameter('Name')) {
                    
                $response->write("Hallo ");
                    
                $response->write($request->getParameter('Name'));
                    
                $response->flush();
                }
                ?>
                Anstatt nun direkt den Front-Controller zu programmieren sollten wir uns erst über dessen Ziel Gedanken machen.
                Wir möchten möglichst flexibel sein, deshalb haben wir Request und Response abstrahiert (anstatt einfach $_REQUEST zu verwenden).

                Der Request enthält immer einen konkreten Auftrag. Alle möglichen Aufträge die unsere Applikation hat
                möchten wir in Objekte kapseln. Dazu kommt das Command-Pattern wie gerufen.

                Das Command-Pattern kapselt einen Auftrag als Objekt. Dadurch wird ermöglicht,
                andere Objekte mit Aufträgen zu parametrisieren, Aufträge in eine Queue zu stellen
                oder diese Rückgängig zumachen.

                (Stephan Schmidt in PHP Design Patterns, O'Reilly)

                Damit ein Befehl arbeiten kann benötigt er das Request und Response Objekt,
                daraus resultiert folgendes Interface
                PHP-Code:
                <?php
                interface Command {
                    public function 
                execute(Request $requestResponse $response);
                }
                ?>
                Für die Erzeugung der einzelnen Seiteninhalte sind also Objekte zuständig, die das Command-Interface implementieren.
                Vorteile:
                1. Die einzelnen Objekte sind austauschbar.
                2. Es können leicht neue Klassen hinzugefügt werden, die neue Inhalte erzeugen.
                3. Ein Seitenaufbau kann von mehr als einem Command-Objekt erzeugt werden,
                  dazu müssen lediglich mehrere Objekte zu einem Zusammengefügt werden.

                Ein Beispiel Command-Objekt:
                PHP-Code:
                <?php
                require_once 'Command.php'// interface

                class HelloWorldCommand implements Command {
                    public function 
                execute(Request $requestResponse $response) {
                        if (
                $request->issetParameter('Name')) {
                            
                $response->write("Hallo ");
                            
                $response->write($request->getParameter('Name'));
                        } else {
                            
                $response->write("Hallo Unbekannter");
                        }
                    }
                }
                ?>
                Dieser kann nun wie im vorhergehenden Beispiel ausgeführt werden:
                PHP-Code:
                <?php
                $request 
                = new HttpRequest();
                $response = new HttpResponse();
                $command = new HelloWorldCommand();
                $command->execute($request$response);
                $response->flush();
                ?>
                Der Teil des Front-Controllers, der Anhand der Anfrage entscheiden kann was getan werden
                soll, ist der CommandResovler. Er soll wie gewünscht Command-Objekte laden, aber wir wollen flexibel bleiben.
                Vielleicht nutzt unsere Applikation später einen anderen Weg Befehle auszuführen.
                Daher benötigen wir zunächst mal ein Interface:
                PHP-Code:
                <?php
                interface CommandResolver {
                    public function 
                getCommand(Request $request);
                }
                ?>
                Alle Klassen, die dieses Interface implementieren, sind in der Lage zu entscheiden, welcher Befehl
                für die Erzeugung der Antwort verwendet werden soll. Dazu verlangt das Interface eine getCommand()
                Methode, die den Request übergeben bekommt und das entsprechende Objekt zurück liefert.
                Die Logik der einzelnen CommandResolver-Klassen kann dabei so komplex sein wie es die
                Applikation verlangt. Für dieses Beispiel genügt eine einfache Implementierung, die nach folgendem Schema arbeitet:
                • Der Name des Commands kann in dem Parameter cmd (für command) übergeben werden
                • Der Wert des cmd-Parameters muss identisch mit dem Namen der Command-Klasse sein
                • Wird kein cmd-Parameter übergeben soll per Default das HelloWorldCommand verwendet werden.

                Die einzelnen Commands werden im Ordner commands/ gespeichert. Los gehts:
                PHP-Code:
                <?php
                require_once 'CommandResolver.php'// interface

                class FileSystemCommandResolver implements CommandResolver {
                    private 
                $path;
                    private 
                $defaultCommand;

                    public function 
                __construct($path$defaultCommand) {
                        
                $this->path $path;
                        
                $this->defaultCommand $defaultCommand;
                    }

                    public function 
                getCommand(Request $request) {
                        if (
                $request->issetParameter('cmd')) {
                            
                $cmdName $request->getParameter('cmd');
                            
                $command $this->loadCommand($cmdName);
                            if (
                $command instanceof Command) {
                                return 
                $command;
                            }
                        }
                        
                $command $this->loadCommand($this->defaultCommand);
                        return 
                $command;
                    }

                    protected function 
                loadCommand($cmdName) {
                        
                $class "{$cmdName}Command";
                        
                $file  "commands/{$cmdName}Command.php";
                        if (!
                file_exists($file)) {
                            return 
                false;
                        }
                        include_once 
                $file;
                        if (!
                class_exists($class)) {
                            return 
                false;
                        }
                        
                $command = new $class();
                        return 
                $command;
                    }
                }
                ?>
                Das letzte Beispiel nun mit dem Resolver:
                PHP-Code:
                <?php
                $request 
                = new HttpRequest();
                $response = new HttpResponse();
                $resolver = new FileSystemCommandResolver('commands''HelloWorld');
                $command $resolver->getCommand($request);
                $command->execute($request$response);
                $response->flush();
                ?
                Nun kann man die Datei mit dem Parameter ?cmd=Command aufrufen um den gewünschten Befehl auszuführen.
                Alle nötigen Schritte sind getan, jetzt können wir endlich den Front-Controller programmieren, unsere Zentrale Schnitstelle:
                PHP-Code:
                <?php
                require_once 'Command.php';
                require_once 
                'Request.php';
                require_once 
                'Response.php';

                class 
                FrontController {

                    private 
                $resolver;

                    public function 
                __construct(CommandResolver $resolver) {
                        
                $this->resolver $resolver;
                    }

                    public function 
                handleRequest(Request $requestResponse $response) {
                        
                $command $this->resolver->getCommand($request);
                        
                $command->execute($request$response);
                        
                $response->flush();
                    }
                }
                ?>
                Anwendung (index.php)
                PHP-Code:
                <?php
                require_once 'FrontController.php';
                require_once 
                'HttpRequest.php';
                require_once 
                'HttpResponse.php';
                require_once 
                'FileSystemCommandResolver.php';

                $resolver = new FileSystemCommandResolver('commands''HelloWorld');
                $controller = new FrontController($resolver);

                $request = new HttpRequest();
                $response = new HttpResponse();

                $controller->handleRequest($request$response);
                ?>
                Den Front-Controller kann man nun noch durch Intercepting-Filter erweitern um Daten
                vor und nach der Verarbeitung durch die Command-Objekte zu verändern.

                Die Codebeispiele und die meisten Erläuterungen stammen aus dem Buch "PHP Design Patterns" von Stephan Schmidt.
                Die Codebeispiele können von der Seite PHP Design Patterns heruntergeladen werden.
                [FONT="Book Antiqua"][I]"Nobody is as smart as everybody" - Kevin Kelly[/I]
                — The best things in life aren't things[/FONT]

                Kommentar


                • #9
                  Danke für deine ausführlichen Erläuterungen. Habe mir das Buch nochmal genauer angeschaut (hattest mir ja einen Link gegeben) und habe mir dann heute nochmal die Zeit genommen, etwas in das Buch reinzulesen. Ich war sehr begeistert, da es wirklich mal etwas ganz anderes ist. Habe es mir auch sogleich zugelegt und bin nun dabei das ganze durch zu arbeiten. Bin jetzt bis zu den Erzeugungsmustern (S.100) vorgedrungen und bis dahin ist noch alles relativ klar, aber ich habe mir aus besonderem Interesse schon das Kapitel über das Registry Patern durchgelesen und habe da noch ein kleines Verständnis Problem.

                  Das Registry Pattern ist, wenn ich das richtig verstanden habe, ein Design Pattern, das dem Programmierer helfen soll, Objekte bzw. Variabeln in einer Applikation (Anwendung) global verfügbar zu machen. Das beste Beispiel sind wohl Konfigurationsdaten oder ein LogHandler-Objekt, die, wie schon im Buch richtig angemerkt, überall in einer Anwendung verfügbar sein müssen. So werden Konfigurationsdaten zur Auswahl des Templates und zur Verbindung zu einer Datenbank benötigt. Dies widerspricht nun aber dem strikten Schichtmodell. Mein Problem ist nun, dass ich nicht verstehe wieso man nicht einfach immer, wie später im betreffenden Kapitel vorgestellt eine SessionRegistry Klasse verwendet. Wan braucht man welche Klasse? Des weiteren verstehe ich nicht, wieso die SessionRegistry Klasse auf der Registry Klasse aufbaut. Zuletzt würde mich interessieren, wie Ihr die ganzen Klassen, die die Registry bzw. die SessionRegistry Klasse lädt, einbinden würdet, denn davon ist dort keine rede. Ist hier eine geschickte Implementierung von __autoload() in der Kombination mit einer clevern Prefix-Vergabe, wie bei PEAR, die passende Lösung?

                  Besonders interessiert hat mich auch das Kapitel über das Template-View-Pattern (habe ich auch schon vorgezogen), da ich bisher eher schief angesehen wurde, wenn ich auf die Template-Eigenschaften von PHP zurückgegriffen habe, wodurch meine Templates großteils wie folgt aussahen.
                  PHP-Code:
                  <html>
                      <head>
                          <title><?= $this->title?></title>
                      </head>
                      <body>
                          <h1 id="masthead"><?= $this->title?></h1>

                          <p class="right">
                              <? if( $this->name !== null ): ?>
                                  Hallo <?= $this->name?>!</p>
                              <? else: ?>
                                  Hallo Gast!
                              <? endif;: ?>
                          </p>

                          <menu>
                              <? foreach( $this->navi as $entry ): ?>
                                 <li><a href="<?= $entry['link']; ?>"><?= $entry['name']; ?></a></li>
                              <? endforeach: ?>
                          </menu>

                          <p id="footer">Copyright © 2004-<?= $this->date?><?= $this->domain?> - All Rights Reserved.</p>
                      </body>
                  </html>
                  Das scheint aber unter Verwendung der Richtigen Template-Klasse ein legitimer und auch gern genommener Weg zu sein. Beruhigt mich doch etwas.

                  Wieder viele Fragen und ich hoffe ich strapaziere eure Nerven nicht zu stark. Ich bin ech sehr dankbar für eure Hilfe.
                  MfG, Andy

                  Kommentar


                  • #10
                    Kann keiner etwas zu meinem Problem mit dem Registry-Design-Pattern etwas sagen?
                    MfG, Andy

                    Kommentar


                    • #11
                      Hallo Andy,

                      ich kann dir leider nicht helfen, da ich das Buch nicht kenne. Solltest du dich für das APF interessieren, kann ich dir gerne die Intension und Verwendung der Registry erläutern.
                      Viele Grüße,
                      Dr.E.

                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                      1. Think about software design [B]before[/B] you start to write code!
                      2. Discuss and review it together with [B]experts[/B]!
                      3. Choose [B]good[/B] tools (-> [URL="http://adventure-php-framework.org/Seite/088-Why-APF"]Adventure PHP Framework (APF)[/URL][URL="http://adventure-php-framework.org"][/URL])!
                      4. Write [I][B]clean and reusable[/B][/I] software only!
                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                      Kommentar


                      • #12
                        Wie wird denn das Registry-Design-Pattern beim APF umgesetzt. Ich denke mal dass die Umsetzung zumindest an das Pattern angelehnt ist. Im allgemeinen verstehe ich ja auch den Sinn (glaube ich), aber leider kann ich eben nicht verstehen, wie die Trennung in SessionRegistry und Registry zu stande kommt. Eine allgemeine Erläuterung des Patterns würde mir schon reichen. Schau mir natürlich auch gern die APF-Komponente an. Allerdings geht es mir hier eigentlich erstmal nur um das allgemeine Verständnis des Pattern.
                        MfG, Andy

                        Kommentar


                        • #13
                          Hallo Andy,

                          Wie wird denn das Registry-Design-Pattern beim APF umgesetzt.
                          Die Registry ist eine Klasse, die über die Methoden register() und retrieve() Werte unterhalb eines Namespaces organisieren kann. Um die Registry überall in gleichem Maße zur Verfügung zu haben ist die Registry als Singleton zu behandeln. Möchtest du Werte zur Verfügung stellen, passiert das mit einem

                          PHP-Code:
                          $Reg = &Singleton::getInstance('Registry');
                          $Reg->register('my::namespace','mykey','myvalue'); 
                          Die Abfrage funktioniert äquivalent dazu per

                          PHP-Code:
                          $Reg = &Singleton::getInstance('Registry');
                          echo 
                          $Reg->retrieve('my::namespace','mykey'); 
                          Dabei kann der Wert beliebig sein (String, Array, Objekt, ...). Weiterhin wird die Registry als Mittel zur globalen Konfiguration verwendet. Einige Komponenten des Frameworks benötigen beispielsweise Informationen darüber, in welchem Umfeld und mit welchem Kontext die Applikation betrieben wird. Diese Parameter werden in der Registry gespeichert und später von Core-Komponenten wieder bezogen.

                          Im allgemeinen verstehe ich ja auch den Sinn (glaube ich), aber leider kann ich eben nicht verstehen, wie die Trennung in SessionRegistry und Registry zu stande kommt.
                          Das ist doch ganz einfach: die SessionRegistry hat über Requests hinweg und innerhalb einer PHP-Session bestand, die Werte der "normalen" Registry sind nur innerhalb eines Requests verfügbar. Was du verwendest, hängt nun vom Anwendungsfall ab. Du könntest dir z.B. überlegen, eine Business-Komponente in eine SessionRegistry zu legen, damit die Initialisierung nicht bei jedem Request erfolgen muss. Diese Performance-Optimierung ist übrigens auch mit der APF-Implementierung der SessionSingleton-Komponente möglich. Mit dieser wird eine Komponente auch über die Session hinweg gehalten. Die Verwendung unterscheidet sich dabei nicht von der des Singleton-Pattern.

                          Allerdings geht es mir hier eigentlich erstmal nur um das allgemeine Verständnis des Pattern.
                          Da würde ich dir wirklich empfehlen, ein Buch über Pattern und deren Anwendung zu lesen. Das hilft nicht nur dir, sondern auch uns, denn das komplette Thema Pattern hier in einem Thread zu diskutieren macht zwar Spass, aber hinterher hat dieser Thread 500 Seiten. Nicht, dass ich mich dagegen wehre, aber dann hast du etwas kompaktes zum Nachlesen. In die Diskussion werde ich mich jedoch gerne einbinrgen.

                          Als Einstieg würde ich dir das Buch "Patterns of enterprise application architecture" von Martin Fowler (bitte auf englisch lesen) empfehlen. Da stehen nicht nur die Pattern beschrieben, sondern auch Anwendungsfälle und wann ein Pattern besser nicht verwendet werden sollte.

                          Schau mir natürlich auch gern die APF-Komponente an.
                          Um dir hierbei ein bischen unter die Arme zu greifen kannst du mal auf den folgenden Seiten vorbei schauen:

                          * Adventure PHP Framework - Klassenreferenz Registry
                          * Adventure PHP Framework - Klassenreferenz Singleton SessionSingleton
                          * Adventure PHP Framework - Konfiguration

                          Die Seiten beschreiben, wie die Registry und das Thema SessionSingleton angeboten wird und verwendet werden kann.
                          Viele Grüße,
                          Dr.E.

                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                          1. Think about software design [B]before[/B] you start to write code!
                          2. Discuss and review it together with [B]experts[/B]!
                          3. Choose [B]good[/B] tools (-> [URL="http://adventure-php-framework.org/Seite/088-Why-APF"]Adventure PHP Framework (APF)[/URL][URL="http://adventure-php-framework.org"][/URL])!
                          4. Write [I][B]clean and reusable[/B][/I] software only!
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                          Kommentar


                          • #14
                            Danke für deine Hilfe und die Links. Wie bereits erwähnt, habe ich mir das Buch PHP Design Pattern zugelegt. Dies arbeite ich zur Zeit auch Schritt für Schritt durch und versuche die verschiedenen Pattern, ihr Einsatzgebiet und ihre Vor- und Nachteile zu verstehen. Zudem werde ich, wenn ich das richtig gesehen habe, Design Pattern auch in meiner Informatik I Vorlesung anschneiden. Dort wird dieses Thema natürlich etwas allgemeiner und losgelöst von Programmiersprachen und Anwendungsbeispielen behandelt. In diesem Thread erhoffe ich mir Antworten auf offen gebliebene Fragen. Zu meinem Erfreuen, hast du dich ja bereits als motivierter Diskussions-Partner erklärt. Damit ich aber nicht nur bei der Theorie der einzelnen Pattern hängen bleibe, werde ich, nach Vollendung meines momentanen Projekts, ein kleines und schlichtes Blog-System strukturieren und schreiben, in dem ich die verschiedenen Pattern gezielt einsetzen werde, soweit es eben sinnvoll ist. Learning by doing - wie man so schön sagt - ist für mich persönlich, ein sehr praktikables und angenehmes System. Dafür werde ich jedoch, wenn Fragen oder Probleme auftreten, einen neuen Thread bemühen. Ich hoffe, dass ich dann auch dort auf eure Unterstützung zählen kann. Danke nochmal für die bisherige Hilfe. Werde mich sicher bald mit neuen Fragen und Ansätzen zurück melden.
                            MfG, Andy

                            Kommentar


                            • #15
                              Hallo Andy,

                              Learning by doing - wie man so schön sagt - ist für mich persönlich, ein sehr praktikables und angenehmes System.
                              Was du nicht vergessen solltest: ein fertiges Framework einzusetzen schützt dich keineswegs davor, Pattern verstehen und anwenden zu können. Softwaredesign hat zunächst erst ein mal nichts mit einem Framework zu tun, sondern kommt von Dir. Aus diesem Grund solltest du vielleicht in einer ruhigen Minute nachdenen, ob es nicht doch sinnvoll ist, Teile nicht selbst zu schreiben, sondern dich auch erfahrene Entwickler zu verlassen. Wenn du dir beispielsweise das Gästebuch-Tutorial unter Adventure PHP Framework - Gaestebuch Tutorial ansiehst, ist dort ungefähr 20% Framework-spezifisch, der Rest des Designs ist "frei".

                              Es geht mir nicht darum dir dein Einsatz eines fertigen Frameworks mit aller Gewalt schmackhaft zu machen, ich möchte dir nur mit auf deinen Weg geben, dass Software-Design != Framework ist und dass man auch und gerade beim Einsatz eines Frameworks noch viel über Software-Design und -Architektur lernen muss!
                              Viele Grüße,
                              Dr.E.

                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                              1. Think about software design [B]before[/B] you start to write code!
                              2. Discuss and review it together with [B]experts[/B]!
                              3. Choose [B]good[/B] tools (-> [URL="http://adventure-php-framework.org/Seite/088-Why-APF"]Adventure PHP Framework (APF)[/URL][URL="http://adventure-php-framework.org"][/URL])!
                              4. Write [I][B]clean and reusable[/B][/I] software only!
                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                              Kommentar

                              Lädt...
                              X