Problem
Objekte verwandter Klassen sollen erzeugt werden, so dass die zu verwendende Klasse erst zur Laufzeit festgelegt werden kann.
Motivation
Eine Aufgabe kann auf unterschiedliche Arten, beispielweise unter Verwendung verschiedener Protokolle oder Verfahren, durchgeführt werden. Die einzelnen Implementierungen sollen zur Laufzeit des Programms austauschbar sein.
Lösung
Die gemeinsame Funktionalität der einzelnen Implementierungen wird in
einer abstrakten Basisklasse gekapselt. Die direkte Erzeugung von
Objekten der Kindklassen dieser Basisklasse wird durch die Deklaration
der entsprechenden Konstruktoren als protected oder
private unterbunden. Für die Erzeugung von Objekten
bietet die abstrakte Basisklasse eine statische Methode an, die
anhand des übergebenen Parameters ein Objekt des gewünschten Typs
erzeugt und zur Verfügung stellt.
Anwendungsbeispiele
Betrachten wir als Beispiel einen Online-Shop, der seinen Partnern ein Interface zum Produktkatalog zur Verfügung stellt. Der eine Partner wünscht eine Schnittstelle auf XML-RPC-Basis, ein weiterer würde gerne SOAP nutzen können, während ein dritter Geschäftspartner ein naives Protokoll auf der Basis von HTTP GET und POST verlangt.
Die Programmlogik ist bei den drei Varianten gleich, sie wird also zunächst in einer Basisklasse gekapselt. Von dieser Basisklasse leiten sich drei Klassen, je eine für XML-RPC, SOAP und HTTP GET / POST ab. Diese abgeleiteten Klassen implementieren den jeweiligen Kommunikationsmechanismus.
Bislang haben wir nur mit dem Konzept der Vererbung Coderedundanzen vermieden. Allerdings ist das Anlegen von Instanzen der drei Klassen aus der Applikation heraus noch nicht transparent, die Nutzung komplizierter als nötig, wie Beispiel 5.1 zeigt.
Beispiel 5.1: Verwendung der drei Klassen ohne abstrakte Fabrik
<?php
switch ($type) {
case 'HTTP': {
include_once 'partner_interface/http.php';
$interface = new PartnerInterface_HTTP;
}
break;
case 'SOAP': {
include_once 'partner_interface/soap.php';
$interface = new PartnerInterface_SOAP;
}
break;
case 'XML-RPC': {
include_once 'partner_interface/xml-rpc.php';
$interface = new PartnerInterface_XMLRPC;
}
break;
}
?>
Nachdem die Auswahl des zu erzeugenden Objektes in der Methode
factory() der Klasse PartnerInterface
an zentraler Stelle gekapselt wurde, gestaltet sich die Erzeugung des
passenden Objektes im Kontext des Programmes einfach und flexibel, wie
Beispiel 5.2
zeigt.
Beispiel 5.2: Verwendung der drei Klassen mit abstrakter Fabrik
<?php
$interface = PartnerInterface::factory($type);
?>Beispiel 5.3 zeigt die Implementierung einer abstrakten Fabrik in PHP, Beispiel 5.4 das Grundgerüst einer konkreten Kindklasse, für die die Fabrik Objekte erzeugen kann.
Beispiel 5.3: Die abstrakte Klasse PartnerInterface
<?php
abstract class PartnerInterface {
protected function __construct() {}
public static function factory($type) {
$source = 'PartnerInterface/' . $type . '.php';
if (@require_once($source)) {
$class = 'PartnerInterface_' . $type;
$object = new $class;
return $object;
} else {
throw new Exception(
sprintf(
'Konnte kein Objekt vom Typ %s erzeugen.',
'PartnerInterface_' . $type
)
);
}
}
public abstract function import($data);
public abstract function export();
}
?>Beispiel 5.4: Die konkrete Klasse PartnerInterface_HTTP
<?php
require_once 'PartnerInterface.php';
class PartnerInterface_HTTP extends PartnerInterface {
public function import($data) {
// ...
}
public function export() {
// ...
}
}
?>