am 26. April 2010
Auf heutigen Tag widmen wir uns mal wieder einem Entwurfsmuster. Um ganz genau zu seine einem Erzeugungsmuster, also eine Muster, dass sich darum kümmern Klassen zu instanzieren. So oft “Muster” in einem so kurzen Abschnitt … Respekt! Natürlich kann man jedes Objekt mit “new” erstellen und fertig. Aber das wäre uns ja zu einfach. Naja vielleicht nicht zu einfach, man sollte weiterhin new verwenden, wenn es Sinn macht. Manchmal gibt es aber saubere Methoden.
Nehmen wir uns einfach mal mein Lieblingsbeispiel: den Logger. Wir haben eine Anwendung geschrieben, die einen Logger benötigt, um alle wichtigen Dinge in eine Textdatei zu schreiben. Sollte ja jedem vertraut klingen. Um jetzt nicht wild mit perfekten Beispielen rumzuwerfen nehmen wir einfach was sehr simples.
<?php
class FileLogger implements Logger
{
// ...
public function log( $message )
{
// ...
}
}
$logger = new FileLogger( 'log.txt' );
// ...
$logger->log( 'Something important has happend' );
// ...
?>
Bis jetzt scheint der Code sauber. Denn wir hantieren mit Interfaces und nutzen den Logger auch wie es sein soll. Das Problem ist nur, dass wir vielleicht den Logger irgendwann austauschen wollen. In Textfiles loggen ist ja so 80er. Wir speichern jetzt alles in eine Datenbank. Eigentlich wäre es doch aber toll, wenn man sowas als eine Art Konfiguration sehen und nicht als Codeanpassung. Vielleicht will ich ja auch mal wieder zurückschwenken oder im Dev-System auf einen EchoLogger zurückgreifen. Das kann in mit einem solchen Code grad mal vergessen.
Wir müssen also verhindern gegen eine konkrete Implementierung zu entwickeln, sondern müssen das ein wenig abstrahieren. Natürlich hilft da das Entwurfsmuster, mit dem wir heute ein wenig rumspielen. Wie vorhin schon angedeutet, werden wir nicht mit new hantieren, sondern die Instanz über eine Fabrik-Methode erstellen. Ich zeig am besten erst mal, wie es aussehen könnte und dann erkläre ich, warum es so aussieht.
<?php
public function loggerFactory( $loggerType, $params )
{
switch $loggerType
{
case 'text':
// ein wenig defensives programmieren hier ...
$logger = new TextLogger( $params['filename' );
break;
case 'db':
// ein wenig defensives programmieren hier ...
$logger = new DbLogger( $params['database_name' /* ... */ );
break;
default:
throw new Exception( 'Logger not found' );
}
return $logger;
}
Jetzt können wir uns ganz einfach einen Logger bauen:
<?php
$config = array( 'logger_type' => 'text', 'filename' => 'log.txt' );
$logger = loggerFactory( $config['logger_type'], $config );
/* @var $logger Logger */
?>
Woher denn jetzt genau diese Config-Datei kommt, da wollen wir gar nicht drauf eingehen.Wichtig ist nur, dass wir jetzt Konfigurieren und nicht mehr eine konkrete Klasse instanzieren. Unser Code ist jetzt also vorbereitet, diese Konfiguration außerhalb des Source-Codes zu betreiben und damit haben wir gewonnen. Meiner Meinung nach haben wir sogar doppelt gewonnen, denn wir haben uns auch noch dazu genötigt, mit Interfaces zu arbeiten, was ja leider in PHP nicht immer an der Tagesordnung ist.