Facebook
Twitter
Google+
Kommentare
7
Willkommen bei "the web hates me". Mittlerweile hat unser Team ein tolles neues Monitoringtool für Agenturen gelauncht. Unter dem Namen koality.io haben wir einen Service geschaffen, der er Agenturen ermöglicht mit sehr geringen Kosten alle Ihre Webseiten zu überwachen.

Projektwerkstatt: Pattern Helper

Heute gibt es mal wieder eine Idee direkt vom PHP Meister Nils. Gut, vielleicht kein Großmeister, aber bestimmt die Person, die in den letzten Monaten am häufigsten über PHP gebloggt hat in ganz Deutschland.

Meine heutige Idee kam mir bei der Arbeit, als ich mal wieder einen Dekorator basteln musste. Wie ihr vielleicht wisst, muss ich beim Dekorierer Entwurfsmuster gegen ein bestimmtes Interface programmieren und die meisten Methoden über Delegation an das Ursprungsobjekt weiterleiten. Methoden, die ich erweitern will würde ich dann einfach erweitern, statt zu delegieren. Wenn ihr meine komische Erklärung nicht versteht, was ich mal vermute, dann schaut euch einfach den Beitrag auf Wikipedia an oder kauft euch das Buch von Stefan Schmidt. Moment da fällt mir ein, dass ich ja selbst schon was geschrieben habe.

Im Moment habe ich das Problem, wenn ich viel mit Delegation arbeite, dass ich viele Methoden stupide weiterleiten, eben delegieren, muss. Habe ich ein Interface mit 20 Methoden, so muss ich die alle in meiner neuen Klasse implementieren. Und genau dies könnte doch eine Tool übernehmen. Als Input bekommt es einen Klassennamen und spuckt ein eine Klasse aus, die nichts macht, als alles weiterzuleiten, was es so an Funktionen gibt.

Machen wir mal ein kleines Beispiel:

interface iBlog
{
  public function schreibeEintrag( $eintrag );
  public function loescheEintrag( $id );
}

Versuchen wir es mal ganz einfach zu halten, da die Idee ja auch ganz einfach ist. Geben wir jetzt dieses Interface oder eine Klasse, die davon abgeleitet wurde in unseren Generator, so erhalten wir folgendes:

class BlogDelegate implements iBlog
{
  private $blog;

  public function __construct( iBlog $blog )
  {
    $this->blog = $blog;
  }

  public function schreibeEintrag( $eintrag )
  {
    return $this->blog->schreibeEintrag( $eintrag );
  }

  public function loescheBeitrag( $id )
  {
    return $this->blog->loescheBeitrag( $id );
  }
}

Während ich den Code tippe, merke ich wie stupide diese Aufgabe wirklich ist. Über Refelction sollte das doch kein Problem sein, vielleicht sollte man auch die IDE mit einbeziehen. Ich meine das Zend Studio hat ja schon die Möglichkeit setter und getter automatisiert zu setzen. So eine delegate-Erstellungs-Methode wäre doch auch toll.

Vielleicht schreibe ich mal sowas als externes Tool, dass man integrieren kann. Mal schauen.

Über den Autor

Nils Langner

Nils Langner ist der Gründer von "the web hates me" und auch der Hauptautor. Im wahren Leben leitet er das Qualitätsmanagementteam im Gruner+Jahr-Digitalbereich und ist somit für Seiten wie stern.de, eltern.de und gala.de aus Qualitätssicht verantwortlich. Nils schreibt seit den Anfängen von phphatesme, welches er ebenfalls gegründet hat, nicht nur für diverse Blogs, sondern auch für Fachmagazine, wie das PHP Magazin, die t3n, die c't oder die iX. Nebenbei ist er noch ein gern gesehener Sprecher auf Konferenzen. Herr Langner schreibt die Texte über sich gerne in der dritten Form.
Kommentare

7 Comments

  1. Mit Hilfe der magic Methode __call könntest du recht einfach die Delegation übernehmen. In deiner __call Methode könntest du mit method_exists prüfen ob die Methode im Zielobjekt existiert und dementsprechend rufen.

    Das ganze kann sogar als abstrakte Klasse implementiert werden, mit dem Vorteil dass du in deinen konkreten Kindklassen manuell die Methoden selbst implementieren könntest falls du eine „Spezialbehandlung“ vornehmen möchtest.

    Reply
  2. @Stefan: Mir der __call Methode hast du das wirkjlich große Problem, dass du auf einmal keine Interfaces mehr verwenden kannst. Denn wenn du die Methoden nicht explizit definierst kackt dir der Interpretor ab.

    @Ingo: Zend Studio kann es auch. Habe nur noch nicht rausgefunden, wie ich mir für dieses Feature einen Shortcut lege.

    Reply
  3. __call ist ein wunderbares Sprachkonstrukt von PHP und hilft nicht nur dabei, dass andere Entwickler eigentlich gar nicht mehr wissen, was die Klasse tatsächlich macht. (Immerhin hat wohl keine IDE der Welt bei einer Klasse ohne ein Interface und ohne konkrete Methoden eine Chance anzuzeigen, welche Methoden die Klasse zur Verfügung stellt.) Auch die Dokumentation der Methoden, die durch __call abgedeckt werden, ist dadurch quasi obsolet geworden.

    Hmm, das ist wohl ein sehr religiöses Thema…

    Reply
  4. @Timo: Wir hatten ja schon mal darüber geredet, dass wir in unserem Projekt schon mal diesen __call Hack angewendet haben. Da war es aber so, dass es keine Interfaces gab und die Klassen eh noch in PHP4 waren, man also Probleme hatte, private von public Methoden zu unterscheiden, was eine unendlich lange Liste von Delegationen bedeutet hätte. Ich finde, da es in einem solchen Fall legitim ist. Man darf aber nie vergessen, dass es trotzdem nicht schön ist. Bei uns heißt er auch wirklich „uglyDecorator“.

    @All: Auf meiner Liste steht noch ein Artikel genau über das Thema.

    Reply
  5. @Timo: Grundsätzliche stimme ich dir natürlich zu. Interfaces sind „sauberer“ und sollten primär Verwendung finden. Das heißt aber nicht dass __call keinen Sinn macht. Beispielweise nutzen wir die _call Methodik um bestimmte Methoden eines Objekts (die einem bestimmten, definierten Interface entsprechen) per Webservice zu ansprechbar zu machen. Ganz grob formuliert delegiert der Webservice Wrapper die Aufrufe via __call an das verknüpfte Objekt. In dem Fall arbeiten die Entwickler nicht direkt gegen das Wrapper Objekt sondern gegen den Webservice. Da es keine Code-Completion für Webservices gibt (oder doch?) ist zumindest in dem Fall dein Argument ausgehebelt (imho) 🙂

    Reply
  6. Hallo Stephan,

    es gibt immer Ausnahmen und deshalb sicherlich Situationen, in denen die Verwendung von __call der richtige Weg ist. Wichtig ist es meiner Meinung nach, die Nachteile zu kennen und sich über die Konsequenzen im Klaren zu sein.

    Für die Definition und Dokumentation von Webservices gibt es je nach Service andere Werkzeuge. Beispielsweise bietet SOAP mit der WSDL eine wunderbare Möglichkeit, Schnittstellen zu definieren. In Programmiersprachen wie Java gibt es Tools, die aus WSDL-Definitionen Stub-Klassen generieren, die dann auch wieder Code-Completion für die Entwicklungsumgebung bieten. Ob es soetwas für PHP auch gibt, weiß ich jedoch nicht.

    Fazit: Natürlich ist die Verwengung von __call *nicht immer* schlecht, es ist jedoch angebracht 2x darüber nachzudenken, ob es wirklich die beste Lösung ist.

    Grüße,
    Timo

    Reply

Leave a Comment.

Link erfolgreich vorgeschlagen.

Vielen Dank, dass du einen Link vorgeschlagen hast. Wir werden ihn sobald wie möglich prüfen. Schließen