dedlfix: OOP: Design Patterns (Factory Method, Dependency Injection, ...)

Beitrag lesen

Hi!

Da das Framework von der Implementierung her natürlich in sich konstistent sein und auch in Zukunft noch den Anforderungen gerecht werden soll, stellt sich jetzt die Frage, welche Design Patterns für viele Komponenten verwendet werden sollen. Eine zum jetzigen, frühen Zeitpunkt durchdachte Wahl erspart später sicherlich viel Arbeit.

Durchdacht für welche konkreten Einsatzzwecke? Die Aufgabenstellung ist ja recht allgemein formuliert, also kann man auch nur eine allgemeine Regel wie zum Beispiel die 80/20 heranziehen. Nur sollte man beachten, dass die 20 nicht heißt, 20% der potentiellen Anwendungsfälle unmöglich zu machen, indem man genau die 80% Fälle abdeckt und den Rest verhindert. Besser ist, 80% abzudecken und für den Rest erweiterbar bleiben - also Plugins vorsehen, "final" und andere abschließenden Dinge sehr gut bedenken. Ich kann mich nicht erinnern, final/sealed schon verwendet zu haben, bin aber schon mehrfach an einer Erweiterungsmöglichkeit gescheitert, weil Framework-Konponenten sich derart abgeschlossen und die eigentlich interessante Arbeit in interne Methoden versteckt hatten, dass ich sie gleich neu schreiben hätte müssen. (War kein PHP, also kein offener Quelltext, aber mit Reflection konnte man sich den inneren Aufbau anschauen.)

Um das "Problem" zu verdeutlichen, möchte ich von einer Klasse ausgehen, die Konfigurationsdaten aus verschiedenen Quellen (z.B. INI, XML, ...) lädt und anschließend für späteren Zugriff speichert. Folgende Möglichkeiten zur Umsetzung haben wir uns bisher überlegt:

Wichtig ist nicht nur, welche Entwurfsmuster eingesetzt werden, sondern dass man sich auch ein Bild macht, welche Arten der Speicherung es gibt und wie kann man die mit einer sehr allgemeinen Datenstruktur flexibel nachbilden. XML ist vielleicht das Format mit den komplexesten Möglichkeiten. Es gibt Elemente mit Inhalt und nebenher lassen sich noch Attribute in den Elemente unterbingen. Verschachtlungen bis zum Umfallen und Namespaces sind weitere Möglichkeiten. Selbst .ini ist nicht nur Name-Value. Es lässt sich durch Sections gruppieren und über Benennungskonventionen nahezu beliebig schachteln. Wenn

[foo]
  a = ...
  b = ...

nicht reicht, erweitert man es eben auf solch eine Art:

[foo]
  bar.a = ...
  bar.b = ...
  qux.a = ...
  qux.b = ...

Wenn man nun mit einer Config-Klasse daherkommt, die nur Name-Value-Pärchen unterstützt, hat man vermutlich noch nicht mal die 80% erreicht.

  1. Einzelne Klasse
    Die Klasse enthält lediglich je eine eigene Funktion für die möglichen Quellen.

Quick and dirty. Lässt sich eigentlich nur über Code-Hinzufügen erweitern. Erben und neue Quellen-Methode hinzufügen geht auch, aber dazu muss der Aufruf der Quellen-Methode per Hand erfolgen oder deren Name konfigurierbar sein. Sicherlich effektiv, aber künstlerisch nicht besonders wertvoll. Scheitert, wenn der Original-Code wegen OpCode-Compilern und anderen Verschleierungsmethoden nicht geändert werden kann.

  1. Vererbung
    Unterklasse ist beispielsweise "Ini", Oberklasse "Config". "Ini" parsed die Ini-Datei und speichert die Konfigurationen wegen der Vererbung "quasi in sich selbst". "Config" ist als abstrakt definiert, sodass nur "Ini" direkt aufgerufen werden kann. Es werden also die einzelnen Funktionen aus 1) in eine eigene Klasse ausgegliedert.

Schon besser im Hinblick auf Erweiterbarkeit und Trennung nach einzelnen Speicherarten. Welche der Unterklassen verwendet werden soll, muss fest im Code stehen oder durch Instantiierung über Stringvariable statt Klassennamen erfolgen (falls man konfigurieren können muss, welche Konfigurationsklasse verwendet werden soll). Für die Instantiierung über Stringvariable scheint sich kein Begriff etabliert zu haben, wie bei variable Variablen. Variable Klassen könnte man das nennen.

  1. "Helper"-Klasse
    Klasse "Ini" implementiert eine Interface "Sources". Im Konstruktor von Klasse "Config" wird der Quellen-Typ ermittelt und dementsprechend eine der Quellen-Klassen, also z.B. "Ini", aufgerufen, welche anschließend ein Array von Konfigurationsdaten zurückliefert. Dieses Array wird anschließend von "Config" gespeichert. ("Ini" erbt nicht von "Config")

Sieht fast nach DI aus. Nur dass du wohl daran denkst, dem Konstruktor einen Wert zu übergeben, über den man die Quellen-Klasse instantiieren kann, statt eines Objekts einer Quellenklasse selbst. Wenn nicht mit variablen Klassen gearbeitet werden soll, ist man hier auch auf einen vordefinierten Vorrat beschränkt.

  1. Factory Method
    Eine Mischung aus 2) und 3). Unterschied: Oberklasse "Config" besitzt eine statische Fabrik-Methode. Statt wie in 2) muss diese bemüht werden, den richtigen "Ansprechpartner" zu finden und nicht der Konstruktor. Der Konstruktor ist in diesem Fall mindestens "protected".

Das ist eher eine Ergänzung zu 2 oder 3. ie Factory legt fest, was verwendet wird. Ob das nun eine Helper- oder eine geerbte Klasse ist, ist dabei nebensächlich. Jedenfalls ist das nicht wirklich testbar - falls das notwendig sein sollte.

  1. Dependency Injection (Verwendet der nun folgende Fall überhaupt dieses Pattern?)
    Eine Mischung aus 3) und 4). Unterschied: Hier bestimmt der Konstruktor von "Config", welcher Quellen-Typ vorliegt und instanziiert die entsprechende Helfer-Klasse. Die Helferklasse erhält eine Referenz von "Config", in die sie mittels der Methoden von "Config" Eigenschaften speichern kann.

Nach meinem Wissen über DI bekommt der Konstruktor bereits eine Instanz der Helperklasse übergeben. Config kann diese dann "melken". Zur Not kann es einen Default-Fall geben, wenn kein Helper-Objekt übergeben wird, erstellt sich der Config-Konstruktor eins selbst. Der Name der physikalischen Konfig-Datenhaltung müsste dann über eine Konvention ermittelt werden.

So wie du das beschreibst, ist (mir) das zu sehr ineinander verzahnt. Dein Konstruktor sieht wie eine Factory aus. Und statt dass Config einfach die Quellen-Objekt-Methoden verwendet, um sich zu füllen, muss sich Config dem von ihm selbst erstellten Quellen-Objekt regelrecht aufdrängen, auf dass es es befülle. Damit machst du nur das Testen von Config- und Quellen-Klassen schwer, weil stets ein (Mock-)Objekt der jeweils anderen Klasse benötigt wird.

Muss das ganze TDD-fähig sein? Muss man Mock-Objekte übergeben können? Aber auch ansonsten sieht mir das am saubersten aus, weil hier nicht mit variablen Klassen hantiert werden muss, um flexibel zu sein.

Das sind verschiedene Möglichkeiten, die wir uns überlegt haben, an die Sache ranzugehen. Alle haben sicherlich Vor- und Nachteile und es gibt sicher auch noch andere Möglichkeiten. Wir sind etwas verwirrt!

Wenn du noch mehr Möglichkeiten kennenlernst, wird das Bild nicht unbedingt klarer.

Also nun meine Frage an Euch: Welche Möglichkeiten haltet Ihr noch für denkbar?

Mit parse_ini_file() und SimpleXML kommt man auch an Konfigurationsdaten ran, aber so einfach und unflexibel willst du es ja nicht haben :-) Komplexer und komplizierter geht immer. Zum Beispiel könnte man statt eines (verschachtelten) Arrays in der Config-Klasse weiteren Klassen vorsehen, die im einfachsten Fall einen Wert aber auch andere individuelle Strukturen enthalten können.

Was würdet Ihr ganz persönlich bevorzugen, wenn Ihr mit einem Framework arbeitet?

Zu allgemein gefragt kann ich da nur zu allgemein antworten. Meine aktuellen Bedürfnisse muss es erfüllen können, notfalls durch individuelle Erweiterbarkeit.

Wir freuen uns über alle Meinungen, Anmerkungen, Vorschläge oder was-auch-immer, was uns bei unserer Entscheidungsfindung behilflich sein könnte.

Schamlos bei anderen abschauen, wie die es gelöst haben, ausprobieren, Erfahrungen sammeln, aus deren Fehlern lernen, was bei anderen zu komplex geraten ist, auf einfache(re) Weise nachbauen, dann selbst um so viel benötigte Funktionalität erweitern, biss es mindestens genauso komplex und unüberschaubar geworden ist, ...

Ps: Sorry, dass das wieder so ein langer Text geworden ist.
Solltet Ihr Fragen zu oben beschriebenem haben, habe ich mich unklar ausgedrückt oder etwas vergessen, so schreibt bitte einfach kurz!

Nö, ich kann auch "lang".

Lo!

0 70

OOP: Design Patterns (Factory Method, Dependency Injection, ...)

Der-Dennis
  • php
  1. 0
    Tom
    1. 0
      Der-Dennis
      1. 0
        Tom
    2. 0
      Tom
      1. 0
        Der-Dennis
    3. 0
      dedlfix
      1. 0
        Tom
        1. 0
          dedlfix
          1. 0
            Der-Dennis
            1. 0
              Der-Dennis
              1. 0
                jobo
                1. 0
                  Tom
                  1. 0
                    Der-Dennis
                    1. 0
                      Tom
                      1. 0
                        Der-Dennis
                        1. 0
                          Tom
                          1. 0
                            Der-Dennis
                            1. 0
                              Tom
                              1. 0
                                Der-Dennis
                2. 0
                  Der-Dennis
            2. 0
              Sven Rautenberg
              1. 0
                jobo
                1. 0
                  Der-Dennis
  2. 0

    Konfigurationsklassen für ein MVC-Framework

    Feldspar
    • programmiertechnik
    1. 0
      Der-Dennis
      1. 0
        Feldspar
        1. 0
          Der-Dennis
  3. 0
    dedlfix
    1. 0
      Der-Dennis
      1. 0
        dedlfix
        1. 0
          Der-Dennis
          1. 0
            dedlfix
            1. 0
              Der-Dennis
              1. 0
                dedlfix
                1. 0
                  Der-Dennis
  4. 0

    OOP: Design Patterns ... -> Zend Framework

    jobo
    1. 0
      Der-Dennis
      1. 0
        jobo
        1. 0
          Der-Dennis
          1. 0
            dedlfix
            1. 0
              jobo
  5. 0
    Sven Rautenberg
    1. 0
      jobo
      1. 1
        Sven Rautenberg
        1. 0
          jobo
          1. 0
            Der-Dennis
    2. 0
      Der-Dennis
      1. 0
        Sven Rautenberg
        1. 0
          Der-Dennis
      2. 0
        dedlfix
        1. 0
          Der-Dennis
          1. 0
            dedlfix
            1. 0
              Der-Dennis
              1. 0
                dedlfix
                1. 0
                  Der-Dennis
                  1. 0
                    dedlfix
                    1. 0
                      Der-Dennis
                      1. 0
                        dedlfix
                        1. 0
                          Der-Dennis
  6. 0

    Bin erst am Montag wieder da

    Der-Dennis
  7. 0
    hotti
    1. 0
      hotti
      1. 0
        Der-Dennis
        1. 0
          hotti
          1. 0
            Der-Dennis
            • perl
          2. 0
            dedlfix
            1. 0
              Der-Dennis
        2. 0
          hotti
          1. 0
            Der-Dennis
            • perl