Composite Pattern

Your rating: None Average: 3.3 (22 votes)

Das Compsosite Pattern (Kompositum - "Zusammengesetztes") ermöglicht es einen Aufruf aus Kommandos verschiedener Klassen zusammenzusetzen. Die Kommandos werden beim Kompositum registriert und bilden zusammen wiederum ein Kommando ab. Dadurch lässt sich eine sehr flexible Schachtelung erreichen.

Dazu zuerst das Interface für das Kompositum und der teilhabenden Klassen.

  1. interface CommandInterface {
  2.  
  3. public function execute($message);
  4. }

Alle Klassen, welche dieses Interface implementieren müssen also eine Methode mit dem Namen execute() besitzen. Diese bekommt in der Übergabe eine entsprechende Fehlermeldung.

Wenn Klassen nur eine Methode execute() und ansonsten nur die Logik zu deren Ausführung beinhalten, wird dies häufig auch als Command Pattern bezeichent.

In meinem konkreten Beispiel wird es darum gehen eine Fehlermeldung aus verschiedenen Ausgabearten zusammenzusetzen. Dazu im folgenden die konkreten Fehlerklassen. Alle Klassen sind nur Beispielhaft implementiert um die Funktionsweise des Composite Patterns zu veranschaulichen.

Error Log

Das schreiben eines Logeintrags. Der Konstrukor erwartet die zu schreibende Datei.

  1. class ErrorLog implements CommandInterface {
  2.  
  3. private $_file;
  4.  
  5. public function __construct($file) {
  6. $this->_file = $file;
  7. }
  8.  
  9. public function execute($message) {
  10. // $message in $file schreiben
  11. }
  12. }

Error Mail

Das Senden einer Email. Der Konstruktor erwartet die Adresse der zu versendenden Email.

  1. class ErrorMail implements CommandInterface {
  2.  
  3. private $_address;
  4.  
  5. public function __construct($address) {
  6. $this->_address = $address;
  7. }
  8.  
  9. public function execute($message) {
  10. // $message per Mail an $address versenden
  11. }
  12. }

Error Sms

Das Senden einer Sms. Der Konstruktor erwartet eine Handynummer.

  1. class ErrorSms implements CommandInterface {
  2.  
  3. private $_number;
  4.  
  5. public function __construct($number) {
  6. $this->_number = $number;
  7. }
  8.  
  9. public function execute($message) {
  10. // $message per Sms an $number versenden
  11. }
  12. }

Error Output

Ausgabe der Nachricht.

  1. class ErrorOutput implements CommandInterface {
  2.  
  3. public function execute($message) {
  4. // z.B. echo $message
  5. }
  6. }

Bis jetzt ist nichts weiter dabei. Jede Klasse enthält eine konkrete Implementierung ihrer Ausgabe. Jetzt gilt es die verschieden Fälle miteinander zu kombinieren. Dazu kommt die eigentlich Composite Klasse zum Einsatz.

Error Composite

Diese Klasse ermöglicht die Kombination der obigen Klassen zu einem Objekt für Fehlermeldungen.

  1. class ErrorComposite implements CommandInterface {
  2.  
  3. private $_errorTypes = array();
  4.  
  5. public function register(CommandInterface $errorType) {
  6.  
  7. $this->_errorTypes[] = $errorType;
  8. }
  9.  
  10. public function execute($message) {
  11.  
  12. foreach ($this->_errorTypes as $errorType) {
  13. $errorType->execute($message);
  14. }
  15. }
  16. }

Die Methode register() enthält einen Type Hint auf das CommandInterface. Dadurch können nur Objekte beim Kompositum registriert werden, welche auch eine execute() Methode beinhalten.

Die execute() Methode des Kompositum führt alle execute() Methoden der bei sich registrierten Error Objekte aus.

So nun noch das ganze Im Einsatz. Es wird für das Beispiel ein normaler, sagen wir mal weniger kritischer Fehler angenommen.

  1. $simpleError = new ErrorComposite();
  2.  
  3. // Logeintrag hinzufügen
  4. $simpleError->register(new ErrorLog('error.log'));
  5.  
  6. // Mailversand hinzufügen
  7. $simpleError->register(new ErrorMail('webmaster@example.com'));
  8.  
  9. // Ausgabe hinzufügen
  10. $simpleError->register(new ErrorOutput());
  11.  
  12. // den Fehler loggen/mailen/ausgeben
  13. $simpleError->execute('Es ist ein unkritischer Fehler aufgetreten.');

Das war ja bis hierher nicht schwer - keine Angst, schwerer wirds auch nicht. Aber das war noch nicht alles. Da die Konstuktion sehr flexibel ist, werden wir jetzt noch einen schwerwiegenden Fehler anlegen, bei dem der Admin zusätzlich mit einer SMS benachrichtigt wird.

  1. $criticalError = new ErrorComposite();
  2.  
  3. // SMS an Admin hinzufügen
  4. $criticalError->register(new ErrorSms(0190123456789));
  5.  
  6. // Email an Admin hinzufügen
  7. $criticalError->register(new ErrorMail('admin@example.com');
  8.  
  9. // Handling des einfachen Fehlers hinzufügen
  10. $criticalError->register($simpleError);
  11.  
  12. // den Fehler loggen,ausgeben,mailen (admin & webmaster),sms (admin)
  13. $criticalError->execute('All your base are belong to us!');

Der kritische Fehler beeinhaltet dadurch das Systemverhalten eines einfachen Fehlers und ergänzt es durch die Information an den Admin als Mail und Sms.

Diese Prinzip ermöglicht es das Composite Pattern in einer Baumstruktur sehr komplex zu gestalten und alle registrierten Objekte mit einem Aufruf auszuführen.

Muss man beim Aufruf des

Muss man beim Aufruf des execute in der foreach Schleife nicht einen Parameter übergeben? Und würde dies nicht zur Folge haben dass lediglich der Übergebene Wert verarbeitet wird anstelle der Werte, die beim instanziiren übergeben wurden? Außerdem schreibt sich dein Interface mit 3 m`s beim Implementieren jedoch nur mit 2.

Macht nicht gerade einen fertigen Eindruck das Teil.

Hallo Gast, vielen Dank, fürs

Hallo Gast,

vielen Dank, fürs aufmerksame Lesen. Ich habe die von dir entdeckten Fehler korrigiert.

Die Verarbeitung bezieht sich auch nur auf den übergebenen Wert, welcher sich im Beispiel auf die $message bezieht. Das Interface stellt sicher, dass jede konkrete Implementierung die gleichen Parameter erwartet. Die zur Instantiierung übergebenen Werte sind spezifisch, da jede Komponente des Kompositums zusätzliche Informationen/Konfigurationen (E-Mail, Tel.-Nr., ..) benötigt um die Nachricht zu verarbeiten. So ist es auch möglich, beispielsweise mehrere Objekte mit verschieden Mail-Adressen in der Struktur zu registrieren.

VG

Schöner Beitrag

Hallo Benjamin,

einen schönen Beitrag hast du da geschrieben.
Inwiefern ich das Composite Pattern verwende, sei erstmal dahin gestellt.
Füge den Link aber vorsichtshalber in meine Favoritenliste.

Viele Grüße,

Alexander

Hallo Alexander, danke für

Hallo Alexander, danke für den Kommentar. Was man verwendet bleibt am Ende jedem selbst überlassen, aber ich denke, wenn man professionell entwickelt kommt man am Composite früher oder später nicht vorbei. Dieses Muster gehört zu meinen persönlichen Favoriten, da sich auf dessen Grundlagen wirklich super Lösungen umsetzen lassen.. ;-)

Composite - Kategorie, Subkategorie

Sehr schöner Artikel, aber leider finde ich das Beispiel über eines Debuggers / Benachrichtigungssystem sehr oft im Internet.

Ich würde gerne eine Webseite reallisieren, wobei die einzelnen Kategorien quasi die Komposition sind und Artikel die Blätter des Baums. So weit so gut - klappt auch so weit das ich nun im PHP Code die einzelnen Kategorien und Artikel anlegen kann, aber wie speicher diese nun persistent in einer relationalen Datenbank? - Über Hinweise würde ich mich freuen.

Ein Gast (E-Mail bitte nicht publizieren) ;-)

Baumstruktur in Datenbank ablegen

Grüße! Erstmal danke für den Beitrag, da freut sich der Autor =) Zu deiner Problematik fällt mir spontan das Thema "Nested Sets" ein. Mit diesem Prinzip können auch sehr komplexe Baumstrukturen in einer Datenbank abgelegt, ausgelesen und bearbeitet werden. Ein Artikel, welcher mir damals weiter half findest du unter http://www.klempert.de/nested_sets/artikel. Aber auch bei Google solltest du eine Menge darüber finden.
Weiterhin könnte auch das "Node" Prinzip des CMS Drupal ein paar gute Gedanken liefern. Jeder Eintrag der Seite (egal ob Artikel, Bild usw.) wird als Knoten in der Datenbank abgelegt. Mit Hilfe des im Core enthaltenem Moduls "Taxonomy" kann eine Baumstruktur/Kategorisierung erreicht werden.
Ich hoffe dir damit einen Wink in die richtige Richtung gegeben zu haben.

Gruß,
Benjamin

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <abbr>
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.

Tags