Was bringt das empfohlene Decorator Pattern

Fragen zu allen Themen rund ums Programmieren außerhalb von phpBB können hier gestellt werden - auch zu anderen Programmiersprachen oder Software wie Webservern und Editoren.
Antworten
mbld
Mitglied
Beiträge: 30
Registriert: 18.10.2016 16:59

Was bringt das empfohlene Decorator Pattern

Beitrag von mbld »

Ich habe ein kleines Problem zu verstehen, wie man vernünftig mit dem Decorator Pattern für Service Decoration umgehen soll.
(Siehe https://area51.phpbb.com/docs/dev/maste ... decoration)

Ich verstehe, dass dies absolut Sinn macht, wenn mehrere Extensions den gleichen Service "überschreiben" wollen.

Allerdings überschreibt man ja in der Regel nur wenige Methoden, und möchte den Rest unverändert lassen.
Hier hätte ich nun erwartet, dass es eine Unterstützung gibt, die bei allen unveränderten Methoden automatisch auf die inner-Klasse delegiert.

Ich hatte also gehofft, dass es reicht, wenn ich für die Methode, die ich verändern will, ungefähr schreibe:
public function a_service_method() {
// ... hier steht meine zusätzliche Funktionalität ...
return $this->inner->a_service_method();
}
und für alle anderen Methoden sollte der Container automatisch die Delegation an $inner übernehmen ohne dass ich jede Methode in meiner Klasse explizit aufführen muss.

Das scheint aber nicht der Fall zu sein. Somit muss ich entweder in meiner dekorierenden Klasse alle Methoden der dekorierten Klasse erneut aufführen und darin jeweils den Aufruf nach $inner delegieren, was sehr umständlich ist und auch fehleranfällig, wenn sich die dekorierte Klasse ändert.

Oder aber ich lasse meine dekorierende Klasse von der dekorierten Klasse (und nicht vom Interface) erben. Damit sind ebenfalls alle Methoden wieder verfügbar. Aber das Decorator-Pattern funktioniert nur noch für die Methoden, die ich überschreibe. Wenn meine Klasse aufgrund einer weiteren Extension gar nicht die Original-Klasse dekoriert, sondern die Klasse aus der Extension, wird in den anderen Methoden nicht die Funktion der dekorierten Klasse, sondern die der Basis-Klasse aufgerufen (da ja die Delegation an $inner fehlt.)

Habe ich da etwas falsch verstanden, oder geht es wirklich nur so umständlich, dass man alle Methoden einzeln aufführen muss und jeweils nichts anderes macht als darin die gleiche Methode von $inner aufzurufen. (Natürlich nur, wenn man auf mehrfaches Dekorieren des gleichen Services durch verschiedene Extensions vorbereitet sein möchte. Andernfalls kann ich den Service einfach überschreiben, und hoffen, dass andere Extensions ihn nicht ebenfalls überschreiben, und die Reihenfolge gewinnen.)
Benutzeravatar
Mike-on-Tour
Supporter
Supporter
Beiträge: 1075
Registriert: 13.01.2020 21:09
Kontaktdaten:

Re: Was bringt das empfohlene Decorator Pattern

Beitrag von Mike-on-Tour »

Ich habe das zwar noch nie ausprobiert, vermute aber mal, dass du in deiner eigenen Klassen-Definition etwas wie
class my_service extends old_class
verwenden musst, um dann die für dich interessanten Funktionen zu überschreiben und zum Schluss die alte Funktion über $inner aufzurufen.

Ist aber wie gesagt nur eine Vermutung und wenn du deinen Code z.B. in der Pastebin einstellst, könnten wir da mal einen Blick drauf werfen.
mbld
Mitglied
Beiträge: 30
Registriert: 18.10.2016 16:59

Re: Was bringt das empfohlene Decorator Pattern

Beitrag von mbld »

Das mit "extends" der dekorierten Klasse führt leider zum totalen Chaos:
Dann arbeitet man nämlich im Prinzip mit zwei Objekten:
1. Ein Objekt der dekorierenden Klasse, das für alle nicht überschriebenen (geerbten) Methoden benutzt wird.
2- Das Inner-Objekt, das immer dann benutzt wird, wenn man eine Methode überschreibt und darin dann nach $this->inner delegiert, um auch die ursprüngliche Funktionalität mit einzubinden.
Wenn nun eine nicht überschriebene Methode eine Objekt-Property ändert, und eine überschriebene Methode diese Property nutzen möchte, dann beziehen sich die beiden Methoden auf verschiedene Objekte und damit auf verschiedene Properties, und das Chaos ist perfekt.

Ganz konkret will ich z.B. in \phpbb\notification\type\post und \phpbb\notification\type\topic in der Methode create_insert_array weitere Daten in die notification schreiben.
Das mache ich einerseits für eine telegram-notification und andererseits für eine Email-Notification. Die beiden Extensions wollte ich aber getrennt halten. Trotzdem sollte die Methode beider Extensions aufgerufen werden. Eigentlich ein typischer Fall für das Decoration-Pattern. Aber eben nur, wenn sich alle anderen Methoden auch auf $this->inner beziehen.

Natürlich kann ich einfach in beiden Extensions jeweils alle benötigten Daten dazufügen, und es ist mir dann egal, welche Methode gewinnt.

Aber sauber ist das nicht, und ich verstehe auch nicht, warum dann der Service-Decorator besser sein soll, als Service-Replacement.

Mit dem Vorschlag die Service-Decoration mit Vererbung zu kombinieren, mache ich letztendlich nichts anderes als Service-Replacement.
Benutzeravatar
Mike-on-Tour
Supporter
Supporter
Beiträge: 1075
Registriert: 13.01.2020 21:09
Kontaktdaten:

Re: Was bringt das empfohlene Decorator Pattern

Beitrag von Mike-on-Tour »

Okay, so weit war ich da noch nicht, und die Beschreibung auf ara51 ist auch nicht sehr ausführlich. Mein Wissen über Service Decoration ist damit erschöpft und ich kann zumindest momentan nicht weiterhelfen.
mbld
Mitglied
Beiträge: 30
Registriert: 18.10.2016 16:59

Re: Was bringt das empfohlene Decorator Pattern

Beitrag von mbld »

Nachdem ich noch ein bisschen experimentiert habe, will ich mal kurz berichten:

Ich will mehrere Notifikations-Typen (phpbb/notification/type/...) erweitern.
Alle Notifikations-Typen implementieren ein gemeinsames Interface, so dass man dieses als Basis für das Dekorator-Pattern nutzen kann, vorausgesetzt alle Verwender der Typen rufen auch nur Interface-Methoden auf.

Das erste Problem ist, dass das Interface mit 25 Methoden sehr umfangreich ist.
Das heißt ich schreibe 25 mal ganz stupide ungefähr folgendes:

Code: Alles auswählen

public function a_service_method($param1, $param2) {
   return $this->inner->a_service_method($param1, $param2);
}
Damit sollte man meinen, dass man erst mal eine funktionierende 1:1 Kopie erzeugt hat.
Dem ist leider nicht so, da der Notifikation-Manager (phpbb/notification/manager.php) leider mindestens an einer Stelle nicht das Interface nutzt, sondern direkt $notification->user_id setzt.
Damit ist zwar im dekorierenden Objekt die Property user_id gesetzt, die Methoden vom inner-Objekt haben aber darauf keinen Zugriff.
Nur weil ich weiß, dass der Notifikation-Manager unmittelbar nach dem setzen der Property die Methode create_insert_array aufruft, kann ich mir mit folgendem Hack behelfen:

Code: Alles auswählen

public function create_insert_array($data, $pre_create_data = []) 
{
    $this->inner->user_id = $this->user_id;
    return $this->inner->create_insert_array($data, $pre_create_data);
}
Ob das die einzige Stelle ist, weiß ich leider nicht. Diese ist wegen duplicate-key-insert ziemlich schnell aufgefallen.

Das Ganze erscheint mir sehr wackelig und ich bin noch unschlüssig, ob ich so weitermachen will.
Was ich damit gewonnen habe, ist dass die Extension tatsächlich mit einer weiteren Extension, die ähnliches macht, zusammenspielt. Aber das auch nur, weil zufällig die Reihenfolge der Service-Dekorator passt.
Die zweite Extension dekoriert nämlich die gleichen Services, erbt aber einfach von den dekorierten Klassen, ohne sich um eine Delegation an die dekorierte Klasse zu kümmern.
Wäre die Reihenfolge der Dekorator umgekehrt, würde meine erste Extension einfach ignoriert werden.
Antworten

Zurück zu „Coding & Technik“