HACK #76 Hack APIs mit dem Façade-Muster vereinfachen Durch die Verwendung des Façade-Entwurfsmusters können Sie die anderen Programmierern zugängliche API vereinfachen. #76 Dies ist ein Muster, von dem ich mir wünsche, dass es viel mehr Programmierer einsetzen, und zwar nicht nur wegen des lustigen Kringels unter dem c in Façade. Die Verwendung des Fassadenmusters zeigt mir, dass ein anderer Programmierer an mich gedacht hat und mir nur die nötigen Informationen bereitstellt (und sonst nichts, was ich möglicherweise kaputtmachen könnte). In Abbildung 7-10 sehen Sie eine einfache API zum Erstellen von Protokollen. log_start(filename) log_message(message) log_end() Log xmllog textlog Log() start(filename) end() instance() XMLLog firstname doc log XMLLog(fileName) close() TextLog filename TextLog(fileName) close() Abbildung 7-10: Die Protokollierungs-API mit einer einfachen Fassade Diese API kann in XML, Text oder in beiden Formaten protokollieren. Wow! Da ich selbst programmiere, beeindruckt mich die Kunstfertigkeit dieses Codes. Scheinbar gibt es für alles eine passende Methode: für den Beginn einer Nachricht, das Hinzufügen von Text, Aufräumen selbst die Behandlung von XML und Text ist einfach. Allerdings würde mich doch sehr interessieren, welche Methode ich wann benutzen soll. Und genau an dieser Stelle kommt das Façade-Muster ins Spiel. Es stellt sicher, dass ich die API korrekt benutze. In unserem Beispiel ist die Fassade die Liste der drei Funktionen ganz links in der Darstellung. Die vertikale Linie besagt in etwa:»ich bin für den Kram auf der rechten Seite zuständig. Ruf einfach meine Methoden auf, und ich erledige den Rest.«Eine Fassade kann aber nicht nur APIs vereinfachen, sondern auch Implementierungsdetails vor einem Client verstecken. Auf diese Weise kann sich die Implementierung komplett verändern, ohne dass die Clients jemals etwas davon mitbekommen. Diese Eigenschaft ist mindestens genauso wichtig wie Hack #76: Das Factory-Method-Entwurfsmuster 335
#76 APIs mit dem Façade-Muster vereinfachen die Vereinfachung, die der Einsatz des Façade-Musters mit sich bringt. Vergessen Sie nicht, dass eine lose Bindung für starke und verlässliche Systeme sorgt. Der Code Sichern Sie den Code aus Beispiel 7-12 in der Datei test.php. Beispiel 7-12: Der Testcode für die Protokoll-API require( "log.php" ); log_start( "mylog" ); log_message( "Starte Programm" ); log_message( "Eine Nachricht für das Protokoll" ); log_message( "Beende Programm" ); log_end( ); Der Code in Beispiel 7-13 gehört in die Datei log.php. Beispiel 7-13: Die Fassade für die Protokollierung require( "log_impl.php" ); function log_start( $filename ) Log::instance( )->start( $filename ); function log_message( $message ) Log::instance( )->add( $message ); function log_end( ) Log::instance( )->end( ); In Beispiel 7-14 finden Sie den Code für log_impl.php. Beispiel 7-14: Die Protokollierungsbibliothek hinter der Fassade class XMLLog private $filename; private $doc; private $log; 336 Kapitel 7: Entwurfsmuster
Beispiel 7-14: Die Protokollierungsbibliothek hinter der Fassade (Fortsetzung) public function XMLLog( $filename ) $this->filename = $filename; $this->doc = new DOMDocument( ); $this->doc->formatoutput = true; $this->log = $this->doc->createelement( "log" ); $this->doc->appendchild( $this->log ); $mess_obj = $this->doc->createelement( "message" ); $text = $this->doc->createtextnode( $message ); $mess_obj->appendchild( $text ); $this->log->appendchild( $mess_obj ); public function close( ) $this->doc->save( $this->filename ); class TextLog private $fh; public function TextLog( $filename ) $this->fh = fopen( $filename, "w" ); fprintf( $this->fh, $message."\n" ); public function close( ) fclose( $this->fh ); class Log private $xmllog = null; private $textlog = null; Hack #76: Das Factory-Method-Entwurfsmuster 337
#76 APIs mit dem Façade-Muster vereinfachen Beispiel 7-14: Die Protokollierungsbibliothek hinter der Fassade (Fortsetzung) public function Log( ) public function start( $filename ) $this->xmllog = new XMLLog( $filename.".xml" ); $this->textlog = new TextLog( $filename.".txt" ); $this->xmllog->add( $message ); $this->textlog->add( $message ); public function end( ) $this->xmllog->close( ); $this->textlog->close( ); public static function instance( ) static $inst = null; if (!isset( $inst ) ) $inst = new Log( ); return $inst; Den Hack ausführen Dieser Hack wird mit dem PHP-Interpreter auf der Kommandozeile ausgeführt: % php test.php % cat mylog.txt Starte Programm Eine Nachricht für das Protokoll Beende Programm % cat mylog.xml <?xml version="1.0" <log> <message>starte Programm</message> <message>eine Nachricht für das Protokoll</message> <message>beende Programm</message> </log> Viel gibt es hier nicht zu sehen. Schließlich sind wir eher am Code selbst und nicht an dessen Ausgaben interessiert. Im Skript test.php wird das Protokoll 338 Kapitel 7: Entwurfsmuster
geöffnet, ein paar Meldungen werden ausgegeben, und schließlich wird das Protokoll wieder geschlossen. Gesteuert wird dies durch die Fassade in log. php. In einer idealen Umgebung wäre das Skript log.php das einzige, auf das»außen stehende«programmierer Zugriff hätten. In der Datei log.php verwenden wir das Singleton-Objekt [Hack #77] Log aus dem Skript log_impl.php, um zwei Logs zu erzeugen. Danach werden die Meldungen an die einzelnen Logs geschickt, die die Nachrichten dann als Text oder XML ausgeben. Hack #76: Das Factory-Method-Entwurfsmuster 339