Olaf Schneider: (PHP5) Klassen mit möglichst wenig Abhängigkeiten verbinden

Hallo OOP- und OOD-erfahrene Programmierer,

ich programmiere ein CMS in php5[1]. Das ganze basiert auf einem eigenen Framework.

Teil 1: Dramatis personae

  • Controller

kümmern sich um die grundliegenden Aufgaben vom Sammeln der Informationen, Ausführen von Aktionen bis zur Seitendarstellung des Ergebnisses.

  • Querybuilder

bauen komplexe SQL-Queries

  • ConfigReader

sammeln Konfigurationsinformationen aus einer xml-Datei. (Welche Tabellen, welche Felder, was ist editierbar, welche 1:n oder n:m-Relationen etc.)

  • XmlContainer

sammelt alle für die Ausgabe nötigen Informationen in einer xml-Struktur (die später per xsl transformiert wird).

Teil 2: Das Drama beginnt

Obwohl einige Controller sich von anderen, teilweise abstrakten Controllern ableiten, bleiben dennoch einige Aufgaben, die immer wieder kehren.

Beispiel: Einen Datensatz des Terminobjektes holen und ins xml schreiben.

Jetzt kommt auch langsam meine Frage ins Spiel: Bei diesen Aufgaben werden mehrere Objekte miteinander verknüpft und dadurch in Abhängigkeit gebracht.

Für das Beispiel müsste eine Klasse Xyz Daten mittels ConfigReader lesen, einen Querybuilder initialisieren, eine Query ausführen und das Ergebnis dem XmlContainer zur Verfügung stellen. Dabei werden schon (mindestens) drei Klassen miteinander verquirlt, die ich eigentlich ganz gerne unabhängig voneinander hätte.

Und jetzt die Frage: Wie macht Ihr das?

  • baut Ihr eine große Helperklasse, die auf alle Objekte Zugriff hat?

  • baut Ihr mehrere Helper, die immer genau zwei Objekte miteinander verbinden?

  • habt Ihr andere/bessere Ideen?

  • Wird alles gut?

Über Antworten, besonders über kompetente ;-) würde ich mich freuen

Gruß
Olaf Schneider

____
[1] Warum tue ich das, obwohl es schon 815 Systeme gibt? Nun:
1: Erfahrungen sammeln und lernen
2: Referenzen vorweisen können
3: Ein System erhalten, welches das tut, was man braucht

  1. Hallo Olaf,

    bin mir nicht sicher, ob ich dich richtig verstehe, aber schau mal hier, vielleicht hilft dir das weiter.

    Eddie

    --
    Old men and far travelers may lie with authority.
    1. Hallo Eddie,

      nein, den Observer suchte ich nicht, sondern eher – wie der Diskussion im Parallelast zu entnehmen ist – die allgemeine Balance zwischen Kapselung und Von oben nach untern runterschreiben.

      Aber, ich gebe zu, dass ich mich nicht allzu klar ausgedrückt habe. Deshalb:

      Nie wieder unterbreche ich eine Marx-Brothers-DVD, um ein neues Thema zu posten,

      verspricht Olaf Schneider

  2. echo $begrüßung;

    ich programmiere ein CMS in php5[1]. Das ganze basiert auf einem eigenen Framework.
    [...] Bei diesen Aufgaben werden mehrere Objekte miteinander verknüpft und dadurch in Abhängigkeit gebracht.
    [...] Dabei werden schon (mindestens) drei Klassen miteinander verquirlt, die ich eigentlich ganz gerne unabhängig voneinander hätte.
    Und jetzt die Frage: Wie macht Ihr das?

    Frameworks, die ich einsetze, sind so gestaltet, dass sie in sich abgeschlossen sind, aber beliebig auf Klassen/Pakete ebendieses Frameworks zugreifen können. Diese Abhängigkeiten sind dokumentiert und müssen, wenn es sich um ein paketbasiertes Framework wie PEAR handelt, bei der Installation berücksichtigt werden.

    Hinzu kommen allgemeine Funktionalitäten, die ich selbst programmiere und quasi eine Erweiterung des obigen Frameworks darstellen, mit diesem also verknüpft und von ihm abhängig sind. Es ist auch schon vorgekommen, dass ich das Framework gewechselt habe (z.B. von PEAR auf Zend Framework (noch immer im Preview-Zustand aber schon brauchbar)). Dann werden einge Funktionalitäten obsolet, andere müssen umgeschrieben werden und weitere neu entwickelt werden.

    Die eigentliche Applikation greift auf beides zu und hat auch noch anwendungsspezifische Klassen, die miteinander agieren. Stellt sich heraus, dass eine Funktionalität so allgemein ist, dass auch andere Anwendungen davon profitieren können, wandert sie einen Absatz nach oben. Ist sie nur allgemein für diese Anwendung kommt sie "nur" in eine Elternklasse.

    • baut Ihr eine große Helperklasse, die auf alle Objekte Zugriff hat?

    Wenn du einen Vermittler zwischenschaltest, bringt das eine weitere Ebene mit zwei Schnittstellen (Daten bekommen und verteilen) ins Spiel, die natürlich auch wieder eine potentielle Fehlerquelle darstellen. Vermeide solche Zwischenschichten wo immer es geht. Nicht vorhandener Code benötigt keinen Pflegeaufwand und keine Ausführungszeit.

    Wenn du Aufgaben mit dem Gedanken separierst, diese zwecks Lastverteilung später auf eine extra Maschine auslagern zu können, so solltest du bedenken, dass ein Prozeduraufruf im selben Prozess um einiges schneller abläuft als ein Zugriff auf einen anderen Prozess oder gar ein Fernzugriff. Deshalb sollten Fernzugriffe so gestaltet sein, dass sie wenig aufgerufen werden müssen. Dementsprechend grob muss die Schnittstelle aussehen. Statt einzelner Zugriffe auf Eigenschaften eines Objekts mit jeweils einem RPC (Remote Procedure Call) ist es günstiger das gesamte Objekt zu übertragen so dass diese Einzelzugriffe lokal stattfinden.

    Eine Abschottung einer Funktionalität durch eine API-Schicht setze ich erst dann ein, wenn es sich aufgrund der Aufgabenstellung nicht vermeiden lasst.

    OOP-gerechtes Programmieren ist chic. Die OOP-Theoretiker denken sich immer mehr Dinge aus, die gut aussehen. Doch nicht immer ist es vom praktischen Standpunkt aus sinnvoll, Dinge, die in reinen OOP-Umgebungen erfunden wurden, auch auf PHP zu übernehmen. PHPs Philisophie ist mitunter einfacher. Man muss beispielsweise keine Collection nachbauen, wenn es ein einfaches Array auch tut.
    Bei jedem Muster, das ich einsetze, frage ich mich, ob im konkreten Fall der Code-Mehraufwand den Einsatz rechtfertigt. Ich habe keine sturen Prinzipien à la: "jede Eigenschaft braucht ein Setter/Getter-Paar". Bei mir bekommt sie das nur wenn es nötig ist und mehr als nur ein Wert gesetzt/gelesen werden soll[*]. Usw. usf. Jeder Codeteil muss sich bei mir die ISDN-Frage gefallen lassen: Ist Sowas Denn Nötig? Die Beantwortung derselben setzt natürlich Erfahrung voraus, die sich bei mir auch aus Erkenntnissen von beschrittenen Irrwegen angesammelt hat.

    echo "$verabschiedung $name";

    [*] In diesen Satz kann man zwei Bedeutungen interpretieren:

    • mehr als ein Wert: also zwei oder mehr
    • es werden noch andere Dinge ausgeführt als (den) Wert(e) zu setzen/lesen
      Beide Bedeutungen sind gemeint.
    1. Hallo dedlfix,

      Frameworks, die ich einsetze, sind so gestaltet, dass sie in sich abgeschlossen sind, aber beliebig auf Klassen/Pakete ebendieses Frameworks zugreifen können. Diese Abhängigkeiten sind dokumentiert und müssen, wenn es sich um ein paketbasiertes Framework wie PEAR handelt, bei der Installation berücksichtigt werden.

      Ähm, PEAR würde ich definitiv nicht als Framework bezeichnen. ;-) Zend Framework, Symfony (/Propel), etc. sind Frameworks, aber PEAR würde ich eher mit CPAN vergleichen, auch wenn der Anspruch / die Organisation nicht vollkommen gleich ist. PEAR ist für mich eine Ansammlung von Bibliotheken, die man in PHP verwenden kann. Es gibt etliche PEAR-Projekte, die ich persönlich nicht einsetzen würde, weil sie in meinen Augen totaler Käse sind. Aber es gibt auch einige sehr nette Dinge, bei denen es sich durchaus lohnt, sich diese anzusehen - auch wenn man schon ein Framework verwendet, es gibt nämlich immer Dinge, die so ein Framework von Haus aus nicht bietet, die man irgendwann vielleicht braucht.

      Zum Rest Deines Postings: Volle Zustimmung.

      Viele Grüße,
      Christian

      --
      "I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone." - Bjarne Stroustrup
      1. Hallo Christian,

        Frameworks, die ich einsetze, sind so gestaltet, dass sie in sich abgeschlossen sind, aber beliebig auf Klassen/Pakete ebendieses Frameworks zugreifen können.

        [...] Zend Framework, Symfony (/Propel), etc. sind Frameworks, [...]

        Das klingt so, als würdest du da ein bisschen was kennen! Kanns du mir vielleicht ein MVC-Framework für PHP5 empfehlen? Gibt es sowas schon?

        Und ganz unabhängig vom speziellen Einsatzzweck: gibt es irgendwas, von dem du so begeistert bist, dass ich es mir auf jeden Fall mal anschauen sollte? Man lernt ja nie aus...

        Danke dir,
        Eddie

        --
        Old men and far travelers may lie with authority.
        1. Hallo Eddie,

          [...] Zend Framework, Symfony (/Propel), etc. sind Frameworks, [...]
          Das klingt so, als würdest du da ein bisschen was kennen!

          Nicht wirklich. Ich hab mir beide mal kurz angesehen, aber wirklich was mit denen gemacht habe ich noch nicht.

          Kanns du mir vielleicht ein MVC-Framework für PHP5 empfehlen? Gibt es sowas schon?

          Naja, sowohl Zend Framework als auch Symfony haben den Anspruch, MVC-Frameworks zu sein, ich hab mir beide aber nicht genau genug angesehen, als dass ich beurteilen kann, ob sie dem Anspruch genügen oder nicht. ;-)

          Und ganz unabhängig vom speziellen Einsatzzweck: gibt es irgendwas, von dem du so begeistert bist, dass ich es mir auf jeden Fall mal anschauen sollte? Man lernt ja nie aus...

          Meinst Du jetzt PEAR-Klassen? Auf Anhieb fielen mir da Image_Graphviz (ok, die Klasse ist nur ein ziemlich simples Interface zu einem externen Programm "graphviz", aber das Programm selbst ist wirklich sehr nett), MDB2 (Datenbankabstraktion), Mail_Mime (Versenden von Mails), Spreadsheet_Excel_Writer (Erzeugen von Excel-Dateien) und PHPUnit2 (Unit-Tests) ein.

          Viele Grüße,
          Christian

          --
          "I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone." - Bjarne Stroustrup
    2. Hallo dedlfix,

      Wenn du einen Vermittler zwischenschaltest, bringt das eine weitere Ebene mit zwei Schnittstellen (Daten bekommen und verteilen) ins Spiel, die natürlich auch wieder eine potentielle Fehlerquelle darstellen. Vermeide solche Zwischenschichten wo immer es geht. Nicht vorhandener Code benötigt keinen Pflegeaufwand und keine Ausführungszeit.

      Mir scheint, dass sich KISS (Keep it simple, stupid.) und DRY (Don't repeat yourself) manchmal widersprechen und man immer eine gute Balance finden muss zwischen „Jedem Bit seine eigene Klasse” und „Von oben nach unten 'runter geschrieben”. Und genau das finde ich manchmal nicht ganz leicht zu entscheiden.

      Beim allein Programmieren hat man immer das Problem der fehlenden Diskussion. Daher weiss man manchmal nicht, welche eigenen Ansätze Allgemeingebrauch sind, welche heiss diskutiert werden (würden) und welche totaler Mumpitz sind.

      Wenn du Aufgaben mit dem Gedanken separierst, diese zwecks Lastverteilung später auf eine extra Maschine auslagern zu können, so solltest du bedenken, dass ein Prozeduraufruf im selben Prozess um einiges schneller abläuft als ein Zugriff auf einen anderen Prozess oder gar ein Fernzugriff. […]

      Das steht zur Zeit nicht zur Debatte, ist aber ein interssanter Aspekt.

      OOP-gerechtes Programmieren ist chic. Die OOP-Theoretiker denken sich immer mehr Dinge aus, die gut aussehen. Doch nicht immer ist es vom praktischen Standpunkt aus sinnvoll, Dinge, die in reinen OOP-Umgebungen erfunden wurden, auch auf PHP zu übernehmen. […]

      Ich versuche schon, die Dinge pragmatisch zu sehen, aber wahrscheinlich muss jeder OO-Programmierer sich einmal im Leben zu Tode designt haben, ehe er mit den OO-Paradigmen ein bisschen entspannter umgeht.

      Gruß und Vielen Dank für alle Antworten,
      Olaf Schneider

      1. echo $begrüßung;

        Mir scheint, dass sich KISS (Keep it simple, stupid.) und DRY (Don't repeat yourself) manchmal widersprechen und man immer eine gute Balance finden muss zwischen „Jedem Bit seine eigene Klasse” und „Von oben nach unten 'runter geschrieben”. Und genau das finde ich manchmal nicht ganz leicht zu entscheiden.

        Bei mir entwickelt sich das oft so, dass ich eine Aufgabe erst einmal "von oben nach unten runterschreibe". Damit ist die grundsätzliche Funktionsweise erstellt. Gibt es nun eine ähnliche Aufgabe, stellt sich schnell heraus, wo die Unterschiede zum Bisherigen sind, und der Code wird nun geeignet ausgelagert. Teile kommen beispielsweise in eine eigene Methode und werden so umgeschrieben, dass die variablen Elemente als Parameter übergeben werden. Außerdem kann es noch passieren, dass sie in eine Basisklasse wandern.

        Beim allein Programmieren hat man immer das Problem der fehlenden Diskussion. Daher weiss man manchmal nicht, welche eigenen Ansätze Allgemeingebrauch sind, welche heiss diskutiert werden (würden) und welche totaler Mumpitz sind.

        Ja, manchmal kommt man mit Diktatur aber schneller voran als mit Demokratie. Was natürlich nicht ausschließt, dass die diktatorische Entscheidung falsch gewesen sein kann. Aber davor ist man bei der Demokratie auch nicht gefeit.

        Nicht alles was im allgemeinen Gebrauch ist, ist auch gut. Tausendmal falsch übernommen, erzeugt möglicherweise den Eindruck eines Standards, kann aber trotzdem ganz falsch sein. Hier hilft nur Erfahrung, mit offenen Augen durch die Programmierwelt laufen und kritisch einschätzen, ob das was andere so produzieren der Weisheit letzer Schluss oder nur Stuss ist. Auch wenn eine Sache von vielen bejubelt wird kann es sein, dass der Autor in Wahrheit nur der Einäugige unter den Blinden ist.

        echo "$verabschiedung $name";

        1. Hallo dedlfix,

          mir ist schon klar, dass die Gruppe, die am größten ist und am lautesten schreit, nicht automatisch recht hat. Aber interessant ist die Meinung anderer Leute immer.

          Deswegen vielen Dank für die Schilderung Deiner Sicht der Dinge.

          Gruß Olaf