Tom: Konfiguration, OOP² oder doch nur Rad² ?

Hello,

so langsam wächst mein kleiner Baukasten für Websites heran. Ja, und er benutzt sogar OOP *g*

Was ich aber bis heute noch nicht ganz klar geregelt habe, sind die Konfigurationsvariablen und -konstanten. Wie baut Ihr eure Konfigurationsdateien?

Wenn man eine mehr oder weniger lose Sammlung von Tools (Klassen) zusammenstellt, die zusammenarbeiten sollen, dann kann es passieren, dass jemand eine Klasse (oder ein Modul) umbenennen muss, da es mit einem bei ihm bereits vorhandenen kollidiert.

Woher erfahren aber dann die davon abhängigen / darauf aufbauenden Module, dass der name sich geändert hat?

Wie kann man dafür Sorge tragen, dass auch die Systemvariablen / Konstanten, die zum Modul gehören, nun einen andern Präfix oder eine anderen Array-Sub-Bezeichner bekommen müssen?

Ich habe mir überlegt, dass man für solche Fälle der Klasse / dem Modul selber eine Methode / Funktion "check_config()" / "modulname_check_config()" einbauen könnte, die eine Musterkonfiguration erzeugt bzw. eine vorhandene ergänzt und checkt.

Ist das jetzt auch überskaliert?

Ich möchte dadurch erreichen, dass bei der ersten Verwendung eines Moduls / einer Klasse automatisch die notwendige Konfiguration erzeugt wird. Das wäre mMn dann OOP², egal, ob man das wirklich objektoriientiert schreibt, oder nur innerhalb des Interpretersystems quasi-objektorientiert anlegt.

Der Verwender der Module oder Klassen könnte sich somit von der Klasse die Arbeit abnehmen lassen.

Oder gibt es sowas schon? Dann wäre es doch nur Rad² ;-))

Liebe Grüße aus dem schönen Oberharz

Tom vom Berg

--
 ☻_
/▌
/ \ Nur selber lernen macht schlau
http://bergpost.annerschbarrich.de
  1. Hallo Tom,
    Bin mir nicht sicher, ob ich Dein Problem richtig verstanden habe.

    Prinzipiell gobt es das Prinzip der Konvention über Konfiguration, welches im Kern besagt, dass man im Idealfall nur die Sachen konfigurieren muss, die man anders haben will, als der angenommene "Default-Fall" es vorsieht.
    Dies entspräche also Deiner Idee, eine Klasse bei initialisierung ihre eigene Konfiguration erzeugen zu lassen.

    Ich weiß nicht, ob es hierfür schon Standard-Mechanismen gibt (zumal ich auch nicht genau wusste, auf welche Programmiersprache Du Dich beziehst, ich tippe mal auf PHP?).

    Wenn Du selber sowas bauen willst, würde ich mir vielleicht mal die Entwurfsmuster Fabrikmethode bzw. Abstrakte Fabrik anschauen - damit könntest Du Dir z.b. "Konfigurationsfabriken" bauen, die je nach Modul, für das die Konfiguration benötigt wird, eigene Konfigurations-Klassen erzeugen können.

    In der Regel braucht man zwar keine verschiedenden Konfigurationsklassen (da man meistens eh nur an Key/Value-Paaren interessiert ist), es kann aber hilfreich sein, um sicher zu stellen, dass bestimmte Werte durch die Konfiguration auf jeden Fall bereit gestellt werden:

      
    # Beispiel: Datenbank-Konfiguration  
    public abstract class AbstrConfigFactory {  
       public static abstract function getInstance();  
       public abstract function createConfiguration();  
    }  
    public class DatabaseConfig () {  
       public __construct ($host, $port, $user, $password,...)  
       public function getHost () {...}  
       public function getPort () {...}  
       public function getUser () {...}  
       public function getPassword () {...}  
       ...  
    }  
      
    public class DatabaseConfigFactory extends AbstrConfigFactory () {  
       private $instance = null;  
       public static function getInstance () {  
          if ($this->instance == null) {  
              $this->instance = new DatabaseConfigFactory();  
          }  
          return($this->instance);  
       }  
       public function createConfiguration () {  
            # Lade Konfigurationsdaten, z.b. von einem File  
            $configHash =  ...  
           return (new DatabaseConfig ($configHash['host'], $configHash['port']...);  
       }  
    }  
      
    #Verwendung:  
    public class FancyWebApplication {  
       #...that needs a database :)  
       public __construct ($databaseConfig) {  
          mysql_connect ($databaseConfig->getHost(), $databaseConfig->getPort()...);  
       ...  
    }  
    $databaseConfig = DatabaseConfigurationFactory.getInstance().createConfiguration();  
    $webApplication = new FancyWebApplication ($databaseConfig);  
      
    
    

    Wenn nun etwas geändert werden muss, z.b. weil nun die Konfiguraionswerte für die DB-Konfig anders heissen soll, so muss nur die Methode "getConfigutation" bzw. "loadConfiguration" in der "DatabaseConfigFactory" modifiziert werden.

    Soweit von mir, hoffe da ist irgendwas brauchbares dabei :)

    Viele Grüße,
    Jörg

    1. Hello Jörg,

      Bin mir nicht sicher, ob ich Dein Problem richtig verstanden habe.

      Doch, Du hast mein Anliegen schon ziemlich genau getroffen.

      Prinzipiell gobt es das Prinzip der Konvention über Konfiguration, welches im Kern besagt, dass man im Idealfall nur die Sachen konfigurieren muss, die man anders haben will, als der angenommene "Default-Fall" es vorsieht.
      Dies entspräche also Deiner Idee, eine Klasse bei initialisierung ihre eigene Konfiguration erzeugen zu lassen.

      Ich weiß nicht, ob es hierfür schon Standard-Mechanismen gibt (zumal ich auch nicht genau wusste, auf welche Programmiersprache Du Dich beziehst, ich tippe mal auf PHP?).

      Prinzipiell wäre die Sprache wahrscheinlich egal, aber hier meinte ich PHP. Das hätte ich dazuschreiben sollen :-O

      Deine Anregungen werde ich mir mal durch den Kopf gehen lassen.
      Es soll dann dabei eine zentrale Datei Konfogurationsdatei entstehen, in die die beteiligten Klassen dann entweder ihre Standardwerte eintragen [1] oder aus der sie ihre veränderten Einstellungen beziehen. Hier kann der Administrator dann also gezielt nachschauen, was er verändern will oder aber auch die Applikation (also vermutlich der User durch sein Handeln [2]).

      [1] Die Standardwerte könnte man auch weglassen, aber es ist ja gerade der Sinn, dass der Administrator sehen kann, was er verstellen kann.

      [2] Wenn nicht auch während des Betriebes Veränderungen sein sollten wären, würde vermutlich eine "Installationsroutine" reichen. Die müsste dann nicht bei jedem Request anspringen.

      Geklärt ist für mich aber noch nicht die Frage, wie ich die Konfiguration der einen Klasse von der der anderen trenne, sodass es keine Konflikte gibt. Aber vermutlich lässt sich das schon durch die Sektionen einer INI-Datei regeln. Die _müssen_ unterschiedliche Namen haben, die darin enthaltenen Keys können jedoch auch in jeder Sektion vorkommen.

      @Dedlfix:
      Es geht nicht um Akzeptanz oder Ablehung _eines_ Frameworks, ondern darum, dass man sich aus vrschiedenen Quellen eines zusammenstellen kann, also z.B. eine Login-Lkasse von A., die BB-Klasse von Christian dazu, die Polnish Notation Calculator-Class von C., Z. oder X....

      Und das ist dann genau das Problem: Austauschbarkeit von Teillösungen. PNC von C. gefällt mir nicht mehr, also nehme ich jetzt die von X. Und schon habe ich das nächste Problem. Ich muss sie ja auch anstöpseln an das Gesamtkunstwerk - brauche also vermutlich ein Interface... *puh*

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
       ☻_
      /▌
      / \ Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
      1. Tach!

        Es geht nicht um Akzeptanz oder Ablehung _eines_ Frameworks, ondern darum, dass man sich aus vrschiedenen Quellen eines zusammenstellen kann, also z.B. eine Login-Lkasse von A., die BB-Klasse von Christian dazu, die Polnish Notation Calculator-Class von C., Z. oder X....

        Okay, gegenüber einem Framework mit festgelegtem Coding-Style ist der größte Nachteil die Individualität der einzelnen Teillösungen. (Coding-Style in Bezug auf Dinge wie Namenskonventionen und nicht beispielsweise Klammernsetzungsstil)

        Und das ist dann genau das Problem: Austauschbarkeit von Teillösungen. PNC von C. gefällt mir nicht mehr, also nehme ich jetzt die von X. Und schon habe ich das nächste Problem. Ich muss sie ja auch anstöpseln an das Gesamtkunstwerk - brauche also vermutlich ein Interface... *puh*

        Ich denke, da muss man rangehen wie bei einem einzuplanenden Austausch des DBMS: man nehme eine Zwischenschicht - Datenabstraktion statt Datenbankabstraktion. Ein Interface im OOP-Sinne wird da nicht helfen, weil du alle Funktionsköpfe etc. der Fremdanbieter vereinheitlichen müsstest. Der Preis für die Zwischenschicht statt Anpassung aller Einbindungen ist natürlich die erhöhte Komplexität des Gesamtsystems.

        dedlfix.

  2. Tach!

    Was ich aber bis heute noch nicht ganz klar geregelt habe, sind die Konfigurationsvariablen und -konstanten. Wie baut Ihr eure Konfigurationsdateien?

    Variablen und Konstanten? Warum nicht eine Konfigurations-Klasse, die aus unterschiedlichen Quellen die Konfigurationsdaten ziehen kann? Wenn mehrere Verwender daran interessiert sind, kann man eine Registry nehmen (quasi ein Singleton).

    Wenn man eine mehr oder weniger lose Sammlung von Tools (Klassen) zusammenstellt, die zusammenarbeiten sollen, dann kann es passieren, dass jemand eine Klasse (oder ein Modul) umbenennen muss, da es mit einem bei ihm bereits vorhandenen kollidiert.
    Woher erfahren aber dann die davon abhängigen / darauf aufbauenden Module, dass der name sich geändert hat?

    Search & Replace im Editor.

    Wie kann man dafür Sorge tragen, dass auch die Systemvariablen / Konstanten, die zum Modul gehören, nun einen andern Präfix oder eine anderen Array-Sub-Bezeichner bekommen müssen?

    Search & Replace im Editor. Oder auf globale Dinge verzichten.

    Ich habe mir überlegt, dass man für solche Fälle der Klasse / dem Modul selber eine Methode / Funktion "check_config()" / "modulname_check_config()" einbauen könnte, die eine Musterkonfiguration erzeugt bzw. eine vorhandene ergänzt und checkt.
    Ist das jetzt auch überskaliert?

    Kann ich nicht beantworten, weil ich nicht erkenne, was du da genau vorhast.

    Ich möchte dadurch erreichen, dass bei der ersten Verwendung eines Moduls / einer Klasse automatisch die notwendige Konfiguration erzeugt wird. [...] Der Verwender der Module oder Klassen könnte sich somit von der Klasse die Arbeit abnehmen lassen.

    Welche Arbeit konkret?

    Oder gibt es sowas schon? Dann wäre es doch nur Rad² ;-))

    NIH*)-Frameworks kommen nicht in die Tüte?

    *) NIH: not invented here.

    dedlfix.

  3. ehlo,

    Was ich aber bis heute noch nicht ganz klar geregelt habe, sind die Konfigurationsvariablen und -konstanten. Wie baut Ihr eure Konfigurationsdateien?

    Eine zentrale ini-Datei mit der Möglichkeit, dezentrale ini's dazulinken zu können. Das ini-Format passt sehr gut zu OOP:

    [OID]
    attr1=value1
    a2=v2

    wobei die OID sowohl eine Sammlung an Konstanten unter sich haben kann, als auch alle Attribute einer URL (== OID). In den Attributen steht dann alles drin, von Title über Description bis hin zum Body oder Template und auch die Berechtigungen.

    In den Attributen steht u.a. auch der Name der Methode, die den Body erzeugt, der Name des Controlers (auch eine Methode) welcher die Eingaben verarbeitet, ergibt sich aus dem Namen der Methode, welche einen Body ohne Eingaben erzeugt. Für die Templates, die ich erst später eingeführt habe, muss der Name des Controlers in einer extra Eigenschaft genannt sein.

    Aufgrund der OIDs ergibt sich eine virtuelle Verzeichnisstruktur, [/], [/foo], [/foo/bar] usw. und über dezentrale ini's ist es auch möglich, Verzeichniszweige in die virtuelle Verzeichnisstruktur einzuhängen (linken). Beispielsweise, wenn im Multi-User-Betrieb bestimmte Verzeichniszweige (Artikel im Shop-Bereich...) von externen Kumpels bearbeitet werden.

    Der Content-Manager ist nur ein Linker, alle dezentralen ini's in eine einzige Binary compiliert in der dann jedes Response-Objekt komplett mit Body oder Template vorliegt. Die Binary schließlich wird vom ResponseHandler (RH) bei jedem Request eingelesen und nach entsprechenden Prüfungen (found, permissions....) ruft der RH nur noch die zugeordneten Methoden auf.

    Das Ganze ist so gebaut, dass eine verständliche Fehlermeldung gezeigt wird, wenn eine Methode nicht vorhanden ist. Das ist mein Plugin-Modell ;)

    Hotti

  4. Moin Tom,

    ich hoffe heute mal auftrumphen zu können :).
    In meinem System gibt es für allgemeine Sachen ein Singleton. Dieses hat sehr viele Methoden, die alle meistens nur einen Wert zurück liefern. Im Moment sind das ganz ganz einfache Konstrukte getMethode{ return "wert";}. Da das ganze aber in einer Methode bzw. in einem Objekt gekapselt ist, könnte ich die Sachen später auch aus der Datenbank holen.

    Dann gibt es noch Spezialfälle. So habe ich z.B. nach dem gleichen Prinzip eine Objekt welches mir Links zusammenbaut. Ich habe z.B. ein Objekt mit den Daten eines Users. Das schiebe ich in die Methode getUserLink und bekomme den Userlink zurück. Damit habe ich alle Links im System zentralisiert. Das alles war eine Reaktion auf das Werk meines Kollegen hier in der Arbeit. Der hat nämlich im kompletten System die Links hardcodiert überall hingeschrieben. PHP, JS, HTML ... alles voller Friendly URL Links. Dann kam irgendwann der Chef und meinte "profil" soll jetzt "profile" heißen. Das hat Stunden gedauert das zu finden und zu ändern.

    In meiner Welt gibt es keine Klassen mit gleichem Namen. Deswegen brauche ich mich mit dem Begriff "Namespace" nicht auseinander zu setzen.

    Gruß
    Namespace / T-Rex

    1. Moin!

      ich hoffe heute mal auftrumphen zu können :).

      Leider nein.

      In meinem System gibt es für allgemeine Sachen ein Singleton.

      Singletons sind sogar schon vom Erfinder des Singleton-Patterns in der GoF offiziell wieder zurückgenommen worden. Du wirst verstehen, wenn ich folglich ausrufe: "Ausgerechnet Singletons!".

      Dieses hat sehr viele Methoden, die alle meistens nur einen Wert zurück liefern.

      Es ist also nicht nur Singleton, sondern auch noch ein Gott-Objekt. Mit so einem haben meine Kollegen und ich derzeit auch zu kämpfen, und keiner ist der Ansicht, sowas wäre eine tolle Idee.

      Im Moment sind das ganz ganz einfache Konstrukte getMethode{ return "wert";}. Da das ganze aber in einer Methode bzw. in einem Objekt gekapselt ist, könnte ich die Sachen später auch aus der Datenbank holen.

      Jaja, INI-Dateien sind dazu ja die Vorstufe (wobei die Frage aufkommen wird: Wie kriegt man die Konfiguration aus der Datenbank ausgelesen, wenn man dazu die Datenbankkonfiguration auslesen muss?). Been there, done that, have the t-shirt.

      Nichts gegen INI-Dateien. Aber gegen Konfigurations-Singletons.

      Dann gibt es noch Spezialfälle. So habe ich z.B. nach dem gleichen Prinzip eine Objekt welches mir Links zusammenbaut. Ich habe z.B. ein Objekt mit den Daten eines Users. Das schiebe ich in die Methode getUserLink und bekomme den Userlink zurück. Damit habe ich alle Links im System zentralisiert. Das alles war eine Reaktion auf das Werk meines Kollegen hier in der Arbeit. Der hat nämlich im kompletten System die Links hardcodiert überall hingeschrieben. PHP, JS, HTML ... alles voller Friendly URL Links. Dann kam irgendwann der Chef und meinte "profil" soll jetzt "profile" heißen. Das hat Stunden gedauert das zu finden und zu ändern.

      Dependency-Injection ist die Methode der Wahl. Dann braucht man auch keine Konfigurations-Singletons.

      In meiner Welt gibt es keine Klassen mit gleichem Namen. Deswegen brauche ich mich mit dem Begriff "Namespace" nicht auseinander zu setzen.

      Stattdessen hast du alles mit dem Singleton gepflastert. Und wenn dein Chef jetzt kommt und sagt: "Die Singletonklasse muss anders heißen", dann sitzt du wieder da und arbeitest an allen Stellen alles um.

      - Sven Rautenberg

      1. Yerf!

        Dependency-Injection ist die Methode der Wahl. Dann braucht man auch keine Konfigurations-Singletons.

        Gibt es für DI eigentlich ein richtig gutes "Standard-Werk" zum lesen? Ich hab da noch ein paar Lücken beim Verständnis...

        Dass die Testbarkeit der Module besser wird ist klar... aber wie wird das ganze konfigurierbar? Momentan sieht es so aus, dass im Live-Betrieb die Objekte mit hardcodierten Abhängigkeiten "injected" werden... das kanns ja nicht sein... wie löst man das elegant?

        Gruß,

        Harlequin

        --
        RIP --- XHTML 2
        nur die Besten sterben jung
        1. Tach!

          [Dependency-Injection]
          Dass die Testbarkeit der Module besser wird ist klar... aber wie wird das ganze konfigurierbar? Momentan sieht es so aus, dass im Live-Betrieb die Objekte mit hardcodierten Abhängigkeiten "injected" werden... das kanns ja nicht sein... wie löst man das elegant?

          Das ist die einfachste Form und die finde ich für den Hausgebrauch durchaus ausreichend. Wenn du ein DI-Framework nimmst, dann sollte dieses die von dir gefragte Konfigurierbarkeit mitbringen. Das heißt, aufgrund deiner Konfiguration holt und übergibt das DI-Framework die passende Abhängigkeit. Im Falle einer Änderung muss man idealerweise nur die Konfiguration anpassen. Natürlich ist ausgewachsene DI nicht immer von Vorteil, denn dadurch kommt wieder mehr Komplexität ins System. (Lohnt sich das im Verhältnis zum Gesamtprojekt sollte man sich da fragen.) Die Konfiguration liegt gemeinhin irgendwo anders als der Code und diese Trennung erhöht nicht unbedingt die leichte Nachvollziehbarkeit.

          dedlfix.

          1. Yerf!

            Das ist die einfachste Form und die finde ich für den Hausgebrauch durchaus ausreichend.

            Ok, so gewinne ich aber "nur" die bessere Testbarkeit (ich will das jetzt nicht kleinreden). Aber der Aufand beim Austausch von Modulen ist da nicht großartig anders. Man muss halt an anderen Codestellen ändern.

            Wenn du ein DI-Framework nimmst, dann sollte dieses die von dir gefragte Konfigurierbarkeit mitbringen. Das heißt, aufgrund deiner Konfiguration holt und übergibt das DI-Framework die passende Abhängigkeit.

            Dann sollte ich mir wohl mal solche Frameworks anschauen. Evtl. lassen sich ein paar gute Ideen ableiten.

            Gruß,

            Harlequin

            --
            RIP --- XHTML 2
            nur die Besten sterben jung
            1. Tach!

              Das ist die einfachste Form und die finde ich für den Hausgebrauch durchaus ausreichend.
              Ok, so gewinne ich aber "nur" die bessere Testbarkeit (ich will das jetzt nicht kleinreden). Aber der Aufand beim Austausch von Modulen ist da nicht großartig anders. Man muss halt an anderen Codestellen ändern.

              Ich sagte ja Hausgebrauch, also wenn man sich (neben der Testbarkeit als wichtiges Argument) die Erweiterbarkeit zwar offen lassen will, aber sowieso meist nur einen Abhängigkeitserfüller (gibts dafür ein Fachwort?) implementiert hat. Dafür fände ich es übertrieben, ein DI-Framework außer zu akademischen Zwecken zu verwenden.

              dedlfix.

        2. Moin!

          Dependency-Injection ist die Methode der Wahl. Dann braucht man auch keine Konfigurations-Singletons.

          Gibt es für DI eigentlich ein richtig gutes "Standard-Werk" zum lesen? Ich hab da noch ein paar Lücken beim Verständnis...

          Dependency Injection ist das simple Vorgehen, einem Objekt, welches ein anderes Objekt braucht, dieses andere Objekt nicht im Inneren per "new" zu erzeugen, sondern als Parameter über den Konstruktor oder eine Setter-Methode hineinzureichen.

          Auf diese Weise kann man außerhalb des Objekts kontrollieren, welches Objekt man hineintut - und kann zum Testen ein passendes Mock-Objekt nehmen, und im Betrieb eines oder eins aus einer Auswahl von mehreren in Frage kommenden Arbeitsobjekten.

          Dass die Testbarkeit der Module besser wird ist klar... aber wie wird das ganze konfigurierbar? Momentan sieht es so aus, dass im Live-Betrieb die Objekte mit hardcodierten Abhängigkeiten "injected" werden... das kanns ja nicht sein... wie löst man das elegant?

          Die Frage ist immer, wie dynamisch man sowas braucht.

          Aber wenn du dir beispielsweise mal Pimple als sehr kleines DI-Framework ansiehst: Das fügt nur relativ geringen Overhead in den Bootstrapping-Prozess ein.

          Bootstrapping musst du ja sowieso bei jeder PHP-Applikation machen - selbst dann, wenn du in einer billig top-down programmierten PHP-Seite nur das geringste bißchen weiteren Code requirest, ist das schon eine Art Bootstrapping.

          Heutige PHP-Applikationen haben einen relativ einheitlichen Bootstrapping-Prozess:
           - Initialisierung des Autoloadings
           - Einlesen der Konfiguration
           - Konfigurieren des DI-Frameworks*
           - Initialisieren der Applikation
           - Und am Ende: Abarbeiten des tatsächlichen Requests

          Die Konfiguration des DI-Frameworks kann einfach oder kompliziert sein, statisch oder dynamisch - was da genau passiert, ist höchst individuell. Mit Pimple (welches ich bislang noch nicht verwenden konnte) definierst du Lambda-Funktionen, die dir das jeweilige Objekt mit seinen Abhängigkeiten erstellen, wobei die Abhängigkeiten wieder durch Lambda-Funktionen aufgelöst werden, oder durch konfigurierte feste Werte. Diese Kette von Funktionsaufrufen wird aber erst zur Laufzeit ausgeführt, wenn eines der Objekte benötigt wird.

          Daraus resultiert logischerweise, dass man am Ende doch irgendeine Art von Gott-Objekt hat, in dem alle diese Abhängigkeiten gespeichert sind. :) Es wird innerhalb der Applikation in einem globalen Kontext erstellt. Aber es wird nicht als statische Klasse betrieben - man könnte sich eine andere DI-Klasse als direkten Ersatz programmieren, wenn man dasselbe Interface implementiert.

          DI-Frameworks sind derzeit in der PHP-Welt noch nicht so sehr verbreitet. Beispielsweise im Zend-Framework 1 fehlt es noch an vernünftigem Support, was wohl primär am Support für PHP vor 5.3 liegen dürfte.

          - Sven Rautenberg

          1. Yerf!

            Dependency Injection ist das simple Vorgehen, einem Objekt, welches ein anderes Objekt braucht, dieses andere Objekt nicht im Inneren per "new" zu erzeugen, sondern als Parameter über den Konstruktor oder eine Setter-Methode hineinzureichen.

            Ok, dann hab ich DI an sich zumindest mal schon verstanden.

            Die Frage ist immer, wie dynamisch man sowas braucht.

            Vor der Frage steh ich noch. Ich denk mal "yagni" ist die beste Antwort auf: "brauch ich es dynamisch?". Falls es sich doch anders herausstellt sollte der Aufwand für den Umbau sich auch in Grenzen halten.

            Bootstrapping musst du ja sowieso bei jeder PHP-Applikation machen

            Ok, ich benutz jetzt kein PHP sondern bin an einer C# Windows Forms Applikation, aber der Ansatz von Pimple ist torzdem ganz interessant.

            Mit Pimple (welches ich bislang noch nicht verwenden konnte) definierst du Lambda-Funktionen, die dir das jeweilige Objekt mit seinen Abhängigkeiten erstellen, wobei die Abhängigkeiten wieder durch Lambda-Funktionen aufgelöst werden, oder durch konfigurierte feste Werte. Diese Kette von Funktionsaufrufen wird aber erst zur Laufzeit ausgeführt, wenn eines der Objekte benötigt wird.

            Sowas in der Art hab ich mir auch schon überlegt. eine zentrale Stelle die abhängig von einer Konfiguration die entsprechenden Objekte liefert. Stellt sich nur die Frage, wie "zentral" diese Stelle sein soll/darf. Mir kam dafür als erstes auch ein Singleton in den Sinn...

            Wäre es besser das "Config-Objekt" per DI zu übergeben? Man müsste es dann nur einmal beim Bootstrap initialisieren und jedes Objekt könnte seine Abhängigkeiten daraus entnehmen. Allerdings wäre das dann auch so ein "Eierlegende-Wollmilch-Sau-Objekt"...

            Gruß,

            Harlequin

            --
            RIP --- XHTML 2
            nur die Besten sterben jung
            1. Moin!

              Bootstrapping musst du ja sowieso bei jeder PHP-Applikation machen

              Ok, ich benutz jetzt kein PHP sondern bin an einer C# Windows Forms Applikation, aber der Ansatz von Pimple ist torzdem ganz interessant.

              Kann C# Lambda-Funktionen?

              Mit Pimple (welches ich bislang noch nicht verwenden konnte) definierst du Lambda-Funktionen, die dir das jeweilige Objekt mit seinen Abhängigkeiten erstellen, wobei die Abhängigkeiten wieder durch Lambda-Funktionen aufgelöst werden, oder durch konfigurierte feste Werte. Diese Kette von Funktionsaufrufen wird aber erst zur Laufzeit ausgeführt, wenn eines der Objekte benötigt wird.

              Sowas in der Art hab ich mir auch schon überlegt. eine zentrale Stelle die abhängig von einer Konfiguration die entsprechenden Objekte liefert. Stellt sich nur die Frage, wie "zentral" diese Stelle sein soll/darf. Mir kam dafür als erstes auch ein Singleton in den Sinn...

              Wäre es besser das "Config-Objekt" per DI zu übergeben? Man müsste es dann nur einmal beim Bootstrap initialisieren und jedes Objekt könnte seine Abhängigkeiten daraus entnehmen. Allerdings wäre das dann auch so ein "Eierlegende-Wollmilch-Sau-Objekt"...

              Nein. Das Config-Objekt liefert ein Interface, um auf einstellbare Werte zuzugreifen. Kein Objekt benötigt ALLE konfigurierten Werte, also gibt es keinen Grund, dem Objekt das ganze Config-Objekt zu übergeben.

              Konfigurierbare Werte sind allerdings ebenfalls Dependencys. Insofern ist im Sinne der Dependency Injection ein konfigurierter Wert ebenso zu injecten, wie die Objektabhängigkeiten. Dabei wird sich vermutlich zeigen, dass zwiebelartig die innersten Objekte von Konfigurationsparametern abhängen, während die äußeren Objekte eigentlich nur von den inneren Objekten abhängen.

              Ich würde es jedenfalls versuchen, so aufzubauen. :)

              - Sven Rautenberg

              1. Yerf!

                Kann C# Lambda-Funktionen?

                Ursprünglich nicht, aber in der aktuellen Version sind sie jetzt mit dabei.

                Nein. Das Config-Objekt liefert ein Interface, um auf einstellbare Werte zuzugreifen. Kein Objekt benötigt ALLE konfigurierten Werte, also gibt es keinen Grund, dem Objekt das ganze Config-Objekt zu übergeben.

                Jo, so gesehen sollte man tatsächlich nicht das ganze Objekt übergeben.

                Konfigurierbare Werte sind allerdings ebenfalls Dependencys. Insofern ist im Sinne der Dependency Injection ein konfigurierter Wert ebenso zu injecten, wie die Objektabhängigkeiten. Dabei wird sich vermutlich zeigen, dass zwiebelartig die innersten Objekte von Konfigurationsparametern abhängen, während die äußeren Objekte eigentlich nur von den inneren Objekten abhängen.

                Das Problem ist das ich zu einem gewissen Grad steuern können will welches die inneren Objekte sind. Ich arbeite da mit Interfaces und (möglicherweise) mehreren Implementierungen. Wobei ich es momentan vielleicht auch nicht zu sehr vertiefen sollte, da ich es aktuell noch nicht wirklich benötige.

                Ich muss da n bissl zwischen meinem Wissensdrang nach dem was möglich ist und dem was ich momentan tatsächlich implementieren sollte trennen...

                Gruß,

                Harlequin

                --
                RIP --- XHTML 2
                nur die Besten sterben jung
          2. hi,

            Dependency Injection ist das simple Vorgehen, einem Objekt, welches ein anderes Objekt braucht, dieses andere Objekt nicht im Inneren per "new" zu erzeugen, sondern als Parameter über den Konstruktor oder eine Setter-Methode hineinzureichen.

            Auf diese Weise kann man außerhalb des Objekts kontrollieren, welches Objekt man hineintut - und kann zum Testen ein passendes Mock-Objekt nehmen, und im Betrieb eines oder eins aus einer Auswahl von mehreren in Frage kommenden Arbeitsobjekten.

            Das bring aber auch gewisse Gefahren mit sich, ein Objekt im Verlauf des Programms von außerhalb her zu ändern. Wenn das DI ist, gefällts mir ganz und gar nicht, da werden interne Abhängigkeiten nach draußen gereicht, die Übersicht geht zum Teufel und Schnittstellen zwischen den Klassen sind nicht mehr klar definiert.

            In meinen privaten Projekten hat sich das Gegenteil von DI bewährt, anstatt das Erbe einer fremden Klasse anzutreten, bevorzuge ich die Delegation ausgewählter Methoden und das erfolgt ganz klar im Konstruktor.:

              
            sub new{  
              my $class = shift;  
              my $self = bless{}, $class;  
              $self->{CGI} = CGI->new;   # CGI-Objekt als Attribut  
              return $self;  
            }  
            
            

            So! Und nun schauen wir mal nach draußen, da steht z.B.:

              
            my $myobj = MyClass->new;  
            $myobj->header;     # rufe eine delegierte Methode  
            $myobj->param;      # rufe noch eine delegierte Methode  
            
            

            Und jetzt kommts ganz dicke: Ich habe monatelang nichts am Code gemacht und muss mal eben nachschauen, wie das kommt, dass $myobj die header-Methode aufrufen kann: Dazu genügt EIN BLick in den Konstruktor, und nicht stundenlanges Wälzen von Programmcode ;)

            Der Vorteil Delegation vs. Erbe: Es werden NICHT alle Methoden geerbt sindern nur die, die gebraucht werden. Das "Überlagern" ist denkbar einfach:

              
            sub param{  
              my $self = shift;  
              return $self->{CGI}->param(@_); # alles drin ;)  
            }  
            
            

            Hotti

            1. Moin!

              Auf diese Weise kann man außerhalb des Objekts kontrollieren, welches Objekt man hineintut - und kann zum Testen ein passendes Mock-Objekt nehmen, und im Betrieb eines oder eins aus einer Auswahl von mehreren in Frage kommenden Arbeitsobjekten.

              Das bring aber auch gewisse Gefahren mit sich, ein Objekt im Verlauf des Programms von außerhalb her zu ändern.

              Konstruktor-Injektion ist deshalb auch mein Favorit.

              Wenn das DI ist, gefällts mir ganz und gar nicht, da werden interne Abhängigkeiten nach draußen gereicht, die Übersicht geht zum Teufel und Schnittstellen zwischen den Klassen sind nicht mehr klar definiert.

              Im Gegenteil.

              - Sven Rautenberg

              1. moin,

                Auf diese Weise kann man außerhalb des Objekts kontrollieren, welches Objekt man hineintut - und kann zum Testen ein passendes Mock-Objekt nehmen, und im Betrieb eines oder eins aus einer Auswahl von mehreren in Frage kommenden Arbeitsobjekten.

                Das bring aber auch gewisse Gefahren mit sich, ein Objekt im Verlauf des Programms von außerhalb her zu ändern.

                Konstruktor-Injektion ist deshalb auch mein Favorit.

                Eine von außerhalb der Klasse erzeugte Abhängigkeit liegt auch in diesem Fall vor und sollte sehr gut dokumentiert sein, sonst suchst Du Dir später die Finger wund ;)

                Darüber hinaus sollte der Anwender der Klasse eine aussagekräftige Fehlermeldung bekommen, wenn er versucht, Methoden aufzurufen, welche die Klasse erst kennt, wenn diese im Konstruktor übergeben werden.

                Hotti

      2. Also ich finde deine Antwort nicht gerade nett. Zumal sie aus vielen Vorurteilen besteht. Ich würde liebend gerne schreiben, dass mein System nicht abhängig von dem Singleton ist. Da dort jedoch ein Array für die Datenbank Verbindung hinterlegt ist... :D. Es ist jetzt aber nicht so das mein System mit dem Singleton "gepflastert" ist.
        Dein Post lässt mich jedoch in einem falschen Licht stehen. Wenn ich mir das so durchlese muss ich wieder an meinen Kollegen denken, der meint er kann OOP nur weil er das Wort "class" schreiben kann. Am Ende kommt strukturelle Programmierung heraus unterstützt mit Objekt Methoden (wobei man da gleich Funktionen nehmen könnte).

        Auf der anderen Seite hast du mich extrem Neugierig gemacht. Nehmen wir mal an es gibt einen Cookie. Der hat eine Lebensdauer von 14 Tage. Die 14 Tage sind jedoch nur Default. Es gibt eine Klasse cCookie welche das setzen des Cookies übernimmt. Über eine Methode bzw. Eigenschaft kann man die Lebensdauer einstellen. Wenn keine Lebensdauer eingestellt wurde, sollen die 14 Tage genommen werden.
        Ich würde das über das "Gott-Singleton" lösen. Wie würdest du es machen? Ich bin enorm gespannt!!!

        Gruß
        Herrscher der Gott-Singletons
        T-Rex

        1. Tach!

          Also ich finde deine Antwort nicht gerade nett.

          Als Fachman legt man sein Augenmerk eher auf fachliche Aussagen als darauf, die Befindlichkeiten des Autors zu berücksichtigen. Nimms nicht persönlich, versuch die Emotion rauszulassen, lies es so, als ob es um die Beurteilung eines von einem Dritten geschriebenen Werkes ginge.

          Zumal sie aus vielen Vorurteilen besteht.

          Oder aus Best-Practice-Prinzipien, hergeleitet aus eigener Erfahrung und angelesenem Wissen. (Wobei Prinzipien nicht unbedingt immer gut sein müssen, wenn man auf ihnen beharrt und damit bessere Lösungen für Einzelfälle ausschließt.)

          Auf der anderen Seite hast du mich extrem Neugierig gemacht. Nehmen wir mal an es gibt einen Cookie. Der hat eine Lebensdauer von 14 Tage. Die 14 Tage sind jedoch nur Default. Es gibt eine Klasse cCookie welche das setzen des Cookies übernimmt. Über eine Methode bzw. Eigenschaft kann man die Lebensdauer einstellen. Wenn keine Lebensdauer eingestellt wurde, sollen die 14 Tage genommen werden.
          Ich würde das über das "Gott-Singleton" lösen. Wie würdest du es machen? Ich bin enorm gespannt!!!

          Wenn du den Wert in einer vom Verwender änderbaren Datei ablegst, kann der dort auch ganz gelöscht werden. Und dann? Woher nimmst du dann den Defaultwert? Ich würde den einfach und schmerzlos als Konstante in der Klasse ablegen. Damit kann man den Wert anderenorts überschreiben, aber es ist weiterhin einer verfügbar. Dass du damit die Klasse aufsuchen muss, wenn du deren Verhalten ändern willst - nunja, das musst du auch, wenn dazu Codeänderungen erforderlich sind.

          dedlfix.

        2. Moin!

          Also ich finde deine Antwort nicht gerade nett.

          Ich habe das System kritisiert, nicht dich.

          Zumal sie aus vielen Vorurteilen besteht.

          Ich kenne nur die Erzähölung aus deinem Posting. Erzähl mehr, und wir können in eine fruchtbare Diskussion einsteigen.

          Ich würde liebend gerne schreiben, dass mein System nicht abhängig von dem Singleton ist. Da dort jedoch ein Array für die Datenbank Verbindung hinterlegt ist... :D. Es ist jetzt aber nicht so das mein System mit dem Singleton "gepflastert" ist.

          Ein einziges Singleton reicht schon, damit das Leben nicht mehr so schön ist.

          Dein Post lässt mich jedoch in einem falschen Licht stehen.

          Dein Post beschreibt, was getan wurde. Ich kritisiere, was getan wurde. Wenn du dafür verantwortlich bist, darfst du dir die Kritik durchlesen, drüber nachdenken, und zu Schlüssen kommen. Ich schreibe dir nicht vor, was das Ergebnis sein soll, aber ich diskutiere gerne mit dir, warum deine Schlüsse derzeit gewisse Dinge, die ich für essentiell halte, nicht ausreichend berücksichtigen.

          Wenn ich mir das so durchlese muss ich wieder an meinen Kollegen denken, der meint er kann OOP nur weil er das Wort "class" schreiben kann. Am Ende kommt strukturelle Programmierung heraus unterstützt mit Objekt Methoden (wobei man da gleich Funktionen nehmen könnte).

          Hast du den Verdacht, dass dein eigener Wissensstand auch nur auf diesem Niveau basiert? Dann sollten wir diskutieren, warum du gewisse Dinge so getan hast, und ich zeige dir gerne die Folgen auf, die daraus resultieren, an die du bislang noch nicht gedacht hast. Möglich, dass die für dich irrelevant sind. Aber fachlicher Austausch hat noch niemanden dümmer gemacht. :)

          Auf der anderen Seite hast du mich extrem Neugierig gemacht. Nehmen wir mal an es gibt einen Cookie. Der hat eine Lebensdauer von 14 Tage. Die 14 Tage sind jedoch nur Default. Es gibt eine Klasse cCookie welche das setzen des Cookies übernimmt. Über eine Methode bzw. Eigenschaft kann man die Lebensdauer einstellen. Wenn keine Lebensdauer eingestellt wurde, sollen die 14 Tage genommen werden.

          Die Lebensdauer eines Cookies ist nichts, was essentiell als Wissen in diese Cookie-Klasse gehört. Die Cookie-Klasse sollte explizit einen vom Programmierer anzugebenden Wert fordern. Der Programmierer muss also "14 Tage" als Zeitdauer hineingeben.

          Damit transformiert sich die Frage zu: Wie konfiguriere ich Werte? Und eventuelle Default-Werte?

          Damit verbunden dann die Frage: Was macht einen Wert so besonders, dass er Default-Wert sein soll? Für welche Dinge ist es denn überhaupt sinnvoll, alternativ zu einem explizit abweichenden Wert ein Default-Verhalten zu implementieren? Kann man nicht einfach zwingend fordern, dass der Programmierer immer einen Wert konfiguriert - und der konfigurierte Wert ist dann der finale Wert? Das macht die Dinge einfacher, weil kein geheimnisvoller Default-Wert ins Spiel kommt, sondern von vorn bis hinten nur ein einziger Wert relevant ist.

          Denn wenn du mit zwei Werten arbeitest, dem "normal konfigurierten" und dem "Default", dann wäre die nächste Frage: Wenn ich den normalen Wert konfigurieren kann, wie konfiguriere ich dann den Default-Wert?

          Und an der Stelle schließt sich der Kreis, und das Sonderbehandeln eines Default-Werts wird sinnlos oder besonders aufwendig. Man kann nicht ZWEI Werte für einen Parameter zur selben Zeit haben, wirksam werden kann immer nur ein einziger.

          Ich mag auch eigene Funktionen/Methoden mit optionalen Parametern nicht wirklich. Wozu sollen die gut sein? In der Regel rufe ich die Funktion immer mit dem vollen Satz an Parametern auf, die alle durch Variablen befüllt werden, die anderswo herkommen. Ich liefere also immer Werte, so dass kein Parameter jemals "optional" ist. Ich kann mir also sparen, den Parameter optional zu machen, weil dieser Anwendungsfall nicht auftritt, und ich spare mir, diesen Sonderfall extra zu testen.

          Ich würde das über das "Gott-Singleton" lösen. Wie würdest du es machen? Ich bin enorm gespannt!!!

          Ich bin sehr dafür, Konfigurationsdaten und Programm strikt zu trennen. Ob nun als Singleton oder sonstwie gelöst: Diese Klasse liest einmal die Konfiguration ein und liefert die darin definierten Werte aus. Fertig.

          Wenn ein Wert nicht definiert ist, existiert er nicht. Die Klasse fügt nicht aufgrund ihres eigenen Programmcodes einen x-beliebigen Konfigurationswert hinzu, der als Default wirkt.

          Daraus folgt, dass man sämtliche Werte, die in der Applikation konfiguriert werden müssen, in der Konfigurationsdatei stehen hat. Damit wird dann auch offensichtlich, von welchen Werten die Applikation in der jeweiligen Umgebung abhängt.

          Davon unabhängig ist es natürlich eine andere Sache, wenn man beispielsweise Pauschal- und Detailkonfigurationen handhaben will:

          cookie.lifetime = 14d
          cookie.lifetime.cart = 365d
          ;cookie.lifetime.voting = ?

          Resultiert in 14 Tagen Cookiegültigkeit für alle Module, der Warenkorb kriegt ein Jahr, und das Voting könnte abweichend konfiguriert sein, ist es aber nicht - also 14 Tage.

          Das Abfragen eines Detailwertes greift also immer auf den Pauschalwert zurück, der zwingend definiert sein muss. Dieser Defaultwert ist aber ebenfalls konfiguriert, er steht nicht irgendwo im Code versteckt. Denn eine Klasse sollte sich immer nur aus einem Grund ändern (Single Responsibility Principle), und wenn das Ändern der gewünschten Default-Cookie-Lifetime dieser Grund ist, wären anderen Gründe, wie z.B. Abändern der Konfigurationsspeicherung etc., überzählige und damit unzulässige Gründe.

          - Sven Rautenberg

          1. Tach!

            Die Lebensdauer eines Cookies ist nichts, was essentiell als Wissen in diese Cookie-Klasse gehört. Die Cookie-Klasse sollte explizit einen vom Programmierer anzugebenden Wert fordern. Der Programmierer muss also "14 Tage" als Zeitdauer hineingeben.

            Speziell beim Cookie bedeutet eine nicht gesetzte Lebensdauer, dass es die Browser-Sitzung nicht überlebt. Systemimmanent ist also anderenorts schon ein Default-Wert festgelegt. Aber wir sollten hier nicht generell über Cookies reden, sondern annehmen, dass der Anwendungsfall hier eine Festlegung der Lebensdauer erfordert und 14 Tage der übliche Standardwert dafür ist.

            Damit verbunden dann die Frage: Was macht einen Wert so besonders, dass er Default-Wert sein soll?

            Es ist ein Wert, der für einen Großteil der Anwender sinnvoll ist, für den Rest aber angepasst werden können soll.

            Für welche Dinge ist es denn überhaupt sinnvoll, alternativ zu einem explizit abweichenden Wert ein Default-Verhalten zu implementieren? Kann man nicht einfach zwingend fordern, dass der Programmierer immer einen Wert konfiguriert - und der konfigurierte Wert ist dann der finale Wert? Das macht die Dinge einfacher, weil kein geheimnisvoller Default-Wert ins Spiel kommt, sondern von vorn bis hinten nur ein einziger Wert relevant ist.

            Geheimnisvoll ist er nur ohne Dokumentation.

            Denn wenn du mit zwei Werten arbeitest, dem "normal konfigurierten" und dem "Default", dann wäre die nächste Frage: Wenn ich den normalen Wert konfigurieren kann, wie konfiguriere ich dann den Default-Wert?

            Ein Default-Wert ist einer der wirkt, wenn nichts angegeben ist. Was soll ein Programm denn machen, wenn in keiner Konfigurationsdatei ein Wert zu finden ist? Radikal solange Fehlermeldungen werfen, bis selbst alle unwichtigen Konfigurationswerte bedient worden sind? Ich denke, mit solch einer "Gängelei" macht man sich weniger Freunde als die Einhaltung solcher Prinzipien wert ist. Ich sehe auch nicht, dass das gängige Praxis ist. Wie machen es dann die Projekte, die von Hinz und Kunz verwendet werden, und bei denen davon ausgegangen werden muss, dass Konfigurationswerte aus den mitgelieferten Dateien gelöscht werden? Umgehen die aus der Sicht des Codes das Problem, indem sie eine interne nicht durch Endanwender änderbare Default-Werte-Konfigurationsdatei haben? Oder wie löst man das Problem des Konkurrenzkampfes zwischen Prinzip und Benutzerfreundlichkeit?

            Und an der Stelle schließt sich der Kreis, und das Sonderbehandeln eines Default-Werts wird sinnlos oder besonders aufwendig. Man kann nicht ZWEI Werte für einen Parameter zur selben Zeit haben, wirksam werden kann immer nur ein einziger.

            Besonders aufwendig ist der ternäre Operator nun auch wieder nicht.

            wert = konfigurierter_wert ?: defaultwert;

            Oder auch die Langform, wenn diese Kurzschreibweise nicht verfügbar ist.

            wert = isset(konfigurierter_wert) ? konfigurierter_wert : defaultwert;

            Ich mag auch eigene Funktionen/Methoden mit optionalen Parametern nicht wirklich. Wozu sollen die gut sein?

            Wozu soll eine pauschale Ablehnung gut sein? Warum muss ich immer etwas angeben, wenn ich nur gelegentlich Abweichungen von einem Standardverhalten haben möchte?

            In der Regel rufe ich die Funktion immer mit dem vollen Satz an Parametern auf, die alle durch Variablen befüllt werden, die anderswo herkommen. Ich liefere also immer Werte, so dass kein Parameter jemals "optional" ist. Ich kann mir also sparen, den Parameter optional zu machen, weil dieser Anwendungsfall nicht auftritt, und ich spare mir, diesen Sonderfall extra zu testen.

            Wie hältst du es bei Sprachen, die verschiedene Methodensignaturen erlauben? Sag jetzt nicht, es gibt bei dir dann nur eine, egal wie lang der Parameter-Rattenschwanz selbst im Default-Fall wird.

            dedlfix.

          2. Manchmal habe ich echt das Gefühl das ich ganz andere Sachen schreibe oder Sage, wenn ich mir nur die Antwort angucke (Vor allem bei meiner Frau...hmpf). Hier ist wieder so ein Punkt:

            Ich habe das System kritisiert, nicht dich.

            Die Antwort würde ich erwarten wenn ich gesagt hätte "du bist nicht nett zu mir". Ich hab aber geschrieben "Also ich finde deine Antwort nicht gerade nett.". Es ist also irrelevant ob du schreibst dass der Autor ein Arsch ist oder sein Buch schlicht Zeitverschwendung sei. Und das ich deine Antwort nicht nett finde liegt wahrscheinlich daran, dass du sehr viel in mein System hineininterpretierst was du jedoch gar nicht wissen kannst. Und nein dass muss man nicht machen nur weil der andere wenig über sein System erzählt.

            Aber mal zur Sache.

            Ein einziges Singleton reicht schon, damit das Leben nicht mehr so schön ist.

            Gutes Argument! Mich verwirrt ein wenig dass du strikt dagegen bist später aber:

            Ich bin sehr dafür, Konfigurationsdaten und Programm strikt zu trennen. Ob nun als Singleton oder sonstwie gelöst: Diese Klasse liest einmal die Konfiguration ein und liefert die darin definierten Werte aus. Fertig.

            Aber vielleicht ist das die Erkenntnis das man nicht strikt gegen etwas sein kann sondern immer den Anwendungsfall sehen muss. Ich hab heute morgen z.B. eine Importdatei geschrieben, welche eine CSV Datei in eine Datenbank einliest. Keine Funktionen, keine Klassen alles von oben nach unten. Am Ende hab ich 100 Zeilen die einfach funktionieren. Was sagst du, wenn ich noch eine Importdatei bräuchte müsste ich alles nochmal machen. Recht hast du. Und soll ich dir was sagen, die 3 Importdateien die ich dieses Jahr gemacht habe sind in der Tat ähnlich. Sollte das ganze irgendwann mal größere Ausmasse annehmen kann man immer noch über OOP nachdenken. Um hier Vorurteilen vorzubeugen sei erwähnt dass dies nicht meiner normalen Herangehensweise entspricht. Ich möchte mit diesem Beispiel nur klar stellen das der Anwendungsfall eventuell Ausnahmen mit sich bringt.

            Ein Defaultwert ist für mich essenziell wichtig. Der Idealfall ist, dass ein Programm immer weiß was der Anwender machen möchte. Das dies nicht realisierbar ist, liegt auf der Hand, es sollte jedoch ein Anstreben sein. Star Trek Fans werden das Wort "Notenergie" wahrscheinlich zugenüge gehört haben. Ein Defaultwert ist der Zustand (Notenergie) in den etwas fällt (z.B. ein Objekt), wenn alle Stricke reißen.

            Vorteil:

            • etwas funktioniert mit möglichst geringer Konfiguration
            • weniger Exception Behandlungen
            • wahrscheinlich einfachere Aufrufe (in deinem Fall müssen ja immer alle Parameter bekannt sein)

            Nachteil:

            • Objekte laufen mit Defaultwerten, auch wenn man das nicht möchte...
              ... als Beispiel bei dem Cookie. Wenn man vergisst einen speziellen Wert zu übergeben wird das Cookie nur 14 Tage leben, obwohl man es 21 Tage am leben halten möchte.

            Die Cookie Klasse würde in Auszügen bei mir so aussehen

              
            class cCookie  
            {  
               $intCookieLifetimeDays = cSystem::init()->getCookieLifetime();  
               public function getCookieLifetime()  
               {  
                  return $this->intCookieLifetimeDays;  
               }  
              
               public function setCookieLifetime($intCookieLifetimeDays)  
               {  
                   $this->intCookieLifetimeDays = $intCookieLifetimeDays  
               }  
            }  
            
            

            Der Defaultwert wird anfangs von meinem Gott-Singleton in das Objekt gesetzt. Das wird im Konstruktor erledigt. Danach hat das Objekt einen Defaultwert. Der kann jederzeit durch die Setter Methode angepasst werden.
            Woher die Singleton Methode den Defaultwert hat ist irrelevant. Der Wert kann aus einer ini Datei kommen oder xml, csv, lma oder aus der Datenbank. Wenn der Wert aus der Datenbank kommen würde, dann könnte er sogar personalisiert sein. Sprich User A bekommt Cockies die 14 Tage halten User B bekommt Cookies für nur 2 Tage (da Verfassungsschützer :D).

            Der Aufruf des Default Cookies ist dann:

              
            new cCookie();  
            
            

            Dein Aufruf nach dem Prinzip dass alle Parameter bekannt sein müssen wäre:

              
            new cCookie(14);  
            
            

            Wenn du jetzt irgendwann den Defaultwert ändern möchtest, darfst du das an x Stellen machen. Außer natürlich du kapselst den Aufruf in irgendeiner Form...also Depency Injection. Das hast du ja als erstes Vorgeschlagen.

            Es gibt also etwas, dass den Aufruf kapselt:

            function cookie()  
            {  
               return new cCookie(14);  
            }  
            
            

            Um jetzt eventuelle Sonderfälle zu behandeln muss das ganze noch über einen Parameter gesteuert werden:

            function cookie(intCookieLifetime = 14)  
            {  
               return new cCookie(intCookieLifetime);  
            }  
            
            

            In meinem Beispiel existiert eine zusätzliche Funktion (könnte auch eine extra Klasse sein), welche sich nur um den Defaultwert kümmert.

            Man könnte natürlich das ganze auch ohne D.I. erledigen:

              
            new cCookie(getCookieLifetime());  
            
            

            Dann hat man jedoch wieder das Problem des Gott-Singletons bzw. verstreuen sich dann die Konfigurationselemente vielleicht im gesamten Sourcecode.

            OOP hin, Martin Fowler her, du kannst es nennen wie du willst, aber ich stehe auf mein Gott-Singleton. Und wenn mein Chef irgendwann mal möchte, dass es anders heißt, dann werde ich deinen Chef dazu anstiften dein Depency Injektion Verwaltungsobjekt anders zu benennen.

            Gruß
            Finger wundgeschriebener
            T-Rex

            1. Tach!

              Und das ich deine Antwort nicht nett finde liegt wahrscheinlich daran, dass du sehr viel in mein System hineininterpretierst was du jedoch gar nicht wissen kannst. Und nein dass muss man nicht machen nur weil der andere wenig über sein System erzählt.

              Der Mangel an Fakten begünstigt aber sehr, dass die Gedanken in eine Richtung abschweifen, die von dir nicht gewünscht ist. Wer sollte es objektiv verdenken? Abgesehen davon kann man natürlich immer noch absichtlich oder versehentlich in die "falsche" Richtung antworten.

              Aber vielleicht ist das die Erkenntnis das man nicht strikt gegen etwas sein kann sondern immer den Anwendungsfall sehen muss.

              Mein Reden, schon seit langer Zeit. Manche scheinen das nicht zu mögen, die hätten gern eine klare Linie statt sich situationsabhängig durch Leben zu schlängeln.

              Ein Defaultwert ist für mich essenziell wichtig. [...]
              Vorteil: [...]

              • weniger Exception Behandlungen

              Das scheint mir eine sehr allgemeine und nicht zutreffende Aussage zu sein. Aber vielleicht gehst du von anderen Voraussetzungen aus als ich. Es ist doch letzlich egal, wo ein Wert herkommt. Wenn er falsch ist, muss er behandelt werden. Gerade wenn du die Default-Werte auswärts lagerst ist die Gefahr einer Änderung in unzulässiger Weise höher als wenn der Wert im Code steht.

              Nachteil:

              • Objekte laufen mit Defaultwerten, auch wenn man das nicht möchte...

              Du meinst aus der Sicht des Anwenders? Nun, sein Problem, Hauptsache ich habs angemessen dokumentiert und nicht vorsätzlich was eingebaut. Er kann ja auch absichtlich was falsch einstellen.

              Die Cookie Klasse würde in Auszügen bei mir so aussehen

              class cCookie

              {
                 $intCookieLifetimeDays = cSystem::init()->getCookieLifetime();
                 public function getCookieLifetime()
                 {
                    return $this->intCookieLifetimeDays;
                 }

              public function setCookieLifetime($intCookieLifetimeDays)
                 {
                     $this->intCookieLifetimeDays = $intCookieLifetimeDays
                 }
              }

                
              Ich finde das ja Overkill, wenn man sich Getter und Setter baut, die nichts weiter als eine 1:1 Variablen-Kapslung sind. Leider kennt PHP keine Propertys à la C#. Die sehen nach außen hin wie einfache Objektvariablen aus und im einfachsten Anwendungsfall ist nur wenig mehr als eine Variablendeklaration zu notieren:  
                
                typ name { get; set; }  
                
              Fertig. Man könnte mit einer einfachen Objektvariable (unter C# Field genannt) anfangen und sie bei Bedarf zu einer Property umbauen (ohne dass sich der Code an den Verwendungsstellen ändern müsste) - YAGNI lässt grüßen - aber wenn es einem C# so einfach macht ... Später kann man bei Bedarf immer noch die Methoden ausbauen. Aber auch PHP hat Tricks auf Lager, die jedoch etwas umständlich zu implementieren sind und damit aufwendiger zu verstehen sind. Man fängt mit einer einfachen Objektvariablen an, und wenn man nie mehr als 1:1-Zugriff braucht, spart man sich Funktionsaufrufe. Will man den Getter oder Setter ausbauen, macht man die Variable private und regelt den Zugriff über [Property Overloading](http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members). In \_\_get()/\_\_set() ruft man die zur Variable gehörenden privaten Zugriffs-Methoden auf. Der Verwender hat weiterhin die Einfachheit eines Variablenzugriffs.  
                
              
              > OOP hin, Martin Fowler her, du kannst es nennen wie du willst, aber ich stehe auf mein Gott-Singleton. Und wenn mein Chef irgendwann mal möchte, dass es anders heißt, dann werde ich deinen Chef dazu anstiften dein Depency Injektion Verwaltungsobjekt anders zu benennen.  
                
              Ein einfaches Umbenennen geht vielleicht noch mit Suchen und Ersetzen, solange keine Laufzeit-Tricks à la variable Variablen mitspielen. Das Wechseln zu einem anderen Framework wäre eher eine Herausforderung.  
                
                
              Nebenbei:  
                
              
              > `new cCookie();`{:.language-php}  
                
              Das c steht für Class? Wenn ja, warum haben deine Methodennamen keinen solchen Präfix? Wenn Variablen einen Typ-Präfix bekommen, welchen bekommt dann $foo im folgenden Beispiel - $intFoo?  
                
                $intFoo = strpos('heuhaufen', 'nadel');  
                
              Und wäre dann  
                
                if ($intFoo === false)  
                  // nicht gefunden  
                
              nicht etwas seltsam? ($mixedFoo fände ich auch nicht sinnvoll, weil solch eine Typenangabe gleich gar keine nützliche Information mehr liefert.)  
                
                
              dedlfix.
              
              1. Also ich bin eher gegen public Eigenschaften. Wobei deine Idee mich da schon wieder in Versuchung führt, da sie gut ist.

                Für mich sind Eigenschaften jedoch etwas privates. Methoden dagegen sind Schnittstellen. Deshalb möchte ich nur über Schnittstellen kommunizieren. Außerdem hab ich da mal einen Artikel über "method chain" gelesen der mich aufhorchen hat lassen.

                Für alle die nicht wissen was "method chain" ist:
                Es gibt Methoden die geben nichts zurück - void. Anstatt nichts zurück zu geben könnte man die instanz des eigenen Objektes zurück geben. Dann kann man gleich die nächste Methode des Objektes aufrufen:
                $objObjekt->methode1()->methode2()

                Der Artikel ging über Java7. Darin soll es einen Chaingkontroll geben, der definiert in welcher Reihenfolge die Methoden aufgerufen werden müssen.
                Als Beispiel:
                Es gibt eine (schlechte) Klasse welche einen Datenbankzugriff regelt. Man muss in ein Objekt dieser Klasse erstmal Datenladen um diese dann speichern zu können, also:
                $objDatabase->setData($arData)->save();

                Andersrum würde es keinen Sinn machen. Die Methoden haben also einen logischen Ablauf. Ruft man die Methoden trotzdem andersrum auf passiert gar eventuell nichts. Es ist ja kein Programmierfehler. Der Compiler oder Interpreter schluckt das ganze. Fehler bekommt man dann bei der Ausführung, wenn z.B. Daten falsch oder gar nicht abgespeichert werden. Deshalb soll man über ein Interface (oder wie auch immer) angeben können in welcher direkten Logischen Reihenfolge die Methoden aufgerufen werden müssen. Also hier wäre der Ausdruck in etwas: save darf erst aufgerufen werden, wenn setData min. 1 mal aufgerufen wurde. Verstößt man gegen diese Angabe meckert der Interpreter oder der Compiler. Man merkt den Fehler also sofort beim testen.

                Deshalb setze ich auf Methoden (und hab mir angewöhnt bei void Methoden ein this zurück zu geben). Deine magischen Methoden würden den oben beschriebenen Ansatz nicht schaffen.
                (bisschen Ausschweifung)

                Ja c steht für Class. Hab mir allerhand Namenskonventionen angewöhnt:
                obj = Objekt
                ar = Array
                str = String
                int = Integer (alle ganzen Zahlen, auch bigint)
                flt = Float (alle Zahlen mit möglichem Komma)
                node = Knoten (xml oder html knoten)
                id = Ids für Knoten (vor allem im Javascript)
                file = Dateihandler
                bool = true / false

                Ich glaube das waren die sinnvollsten. Dein Beispiel wäre natürlich eine Ausnahme. Wenn ich nicht weiß was für ein Typ in einer Variable sein könnte, benutze ich keine Vorangestellten Zeichen. In dem Fall wäre es einfach $foo. Es könnte aber auch sein, dass ich die Namenkonvention mit der größten Wahrscheinlichkeit nehme. Sprich ich erwarte dass $foo ein false ist, werde ich $boolFoo nehmen. Erwarte ich einen Integer dementsprechend $intFoo. Das c für Klassen ist quatsch, da ich jedoch den zweiten Buchstaben großschreibe sieht es optisch einfach ein bisschen besser aus.

                Gruß
                tRex

                1. Tach!

                  Also ich bin eher gegen public Eigenschaften. Wobei deine Idee mich da schon wieder in Versuchung führt, da sie gut ist.
                  Für mich sind Eigenschaften jedoch etwas privates. Methoden dagegen sind Schnittstellen. Deshalb möchte ich nur über Schnittstellen kommunizieren.

                  Mit "meiner" Idee kannst du sogar beides haben, Variablenzugriff (über Overloading) und Getter/Setter-Methoden. Und da fällt mir noch ein, dass eigentlich auch nur eine Methode braucht (falls das nicht gegen SOLID verstößt)

                  function foo($newFoo = null) {
                    if ($newFoo !== null) { // oder func_num_args() befragen, wenn null ein gültiger Wert sein darf
                      // Wertprüfungen etc.
                      $this->foo = $newFoo;
                    }
                    // was sonst noch so fürs Getten zu tun ist
                    return $this->foo;
                  }

                  Kann man als Getter und Setter verwenden, aber nicht fürs Method Chaining. Oder vielleicht doch, wenns dafür undurchsichtiger sein darf. Setzen und Lesen will man ja meist nicht gleichzeitig, also gibt man bei übergebenem Argument das Objekt (fürs Chaining) zurück, ansonsten den Wert.

                  Außerdem hab ich da mal einen Artikel über "method chain" gelesen der mich aufhorchen hat lassen.
                  $objObjekt->methode1()->methode2()

                  Ist nett, darf man aber auch mal kritisch ansehen. Was ist wenn eine Methode ein anderes Objekt zurückgibt. Chainst du dann einfach weiter? Kann man vermutlich nicht definitiv beantworten, ohne einen konkreter Fall vorliegen zu haben und dabei zu sehen, ob es verständlich oder verwirrend ist. - Ja, man darf das ruhig schlecht finden, ich weiß auch nicht, ob ich das ernsthaft so implementieren würde.

                  Der Artikel ging über Java7. Darin soll es einen Chaingkontroll geben, der definiert in welcher Reihenfolge die Methoden aufgerufen werden müssen. [...] Deshalb soll man über ein Interface (oder wie auch immer) angeben können in welcher direkten Logischen Reihenfolge die Methoden aufgerufen werden müssen. Also hier wäre der Ausdruck in etwas: save darf erst aufgerufen werden, wenn setData min. 1 mal aufgerufen wurde. Verstößt man gegen diese Angabe meckert der Interpreter oder der Compiler. Man merkt den Fehler also sofort beim testen.

                  Auch nett, aber nur mit der damit einhergehenden Komplexität zu haben. Wobei die sich vermutlich nur auf die Angabe und das Erstellen der Interfaces beschränkt. Wenn ich mich recht erinnere, definiert man als Rückgabetyp ein Interface und das definiert anhand der erlaubten Nachfolger die aufrufbaren Methoden.

                  Deshalb setze ich auf Methoden (und hab mir angewöhnt bei void Methoden ein this zurück zu geben). Deine magischen Methoden würden den oben beschriebenen Ansatz nicht schaffen.

                  Nur dass du bei PHP keinen Rückgabetyp festlegen kannst und damit kein Compiler die Reihenfolge prüfen kann. Es gibt ja nur Type-Hints bei Funktionsparametern.

                  dedlfix.

                2. hi T-Rex,

                  Für mich sind Eigenschaften jedoch etwas privates. Methoden dagegen sind Schnittstellen. Deshalb möchte ich nur über Schnittstellen kommunizieren.

                  Jow!!!! Das unterstreiche ich ganz dick! Der Grund: Wenn erforderlich (oft erlebt) kann das Objekt komplett umgebaut werden, wobei Attribute intern eine andere Referenz oder auch einen anderen Namen bekommen. Die Methoden jedoch bleiben namentlich als Schnittstellt nach draußen, da ist also nichts zu ändern.

                  Genauso verhält es sich mit privaten Methoden, Perl ist da tolerant, theoretisch besteht die Möglichkeit, private Methoden auch public zu nutzen, aber: Der Autor der Klasse hat private Methods als _solche gekennzeichnet und darf die intern ändern oder gar wegfallen lassen. Wer also im Public-Bereich (main) eine private Methode nutzt, ist selber schuld.

                  Außerdem hab ich da mal einen Artikel über "method chain" gelesen der mich aufhorchen hat lassen.

                  Nicht machen. Scroll mal nach oben... im Prinzip werden Interna damit nach draußen gereicht und das ist schlecht. Ähnlich schlechter Stil ist, wenn im Konstruktor auf Attribute zugegriffen wird, die vorher, z.B. durch Aufruf einer Klassenmethode initiiert werden.

                  Wenn ich es vermeiden kann, werden auch Public-Methoden die Attribute möglichst nicht verändern, dafür gibt es Rückgabewerte der Methoden, die es zu nutzen gilt. Es lässt sich jedoch nicht immer vermeiden, innerhalb einer Methode mal ein Attribut hinzuzufügen, beispielsweise ein Objekt einer völlig fremden Klasse als eigenes Attribut:

                    
                  # Public Method  
                  sub orders{  
                    my $self = shift; # ich bin in meiner Klasse...  
                    $self->{cart} = Cart::Orders->new; # Warenkorb::Bestellung, andere Klasse  
                    my $ords = $self->{cart}->showorders(\&cbo) or $self->errpush($@);  
                    return $ords; # Anzahl der Bestellungen  
                    # angezeigt werden die Bestellungen über die Callbackfunktion &cbo  
                  }  
                  
                  

                  Callbackfunktionen sind ein Thema für sich, in meinem Fall übernimmt die Callbackfunktion die HTML-Ausgabe, CB-Funktionen können aber auch PDFs erstellen oder gleich auf STDOUT schreiben...

                  Hotti

                  1. Tach!

                    Für mich sind Eigenschaften jedoch etwas privates. Methoden dagegen sind Schnittstellen. Deshalb möchte ich nur über Schnittstellen kommunizieren.

                    Jow!!!! Das unterstreiche ich ganz dick! Der Grund: Wenn erforderlich (oft erlebt) kann das Objekt komplett umgebaut werden, wobei Attribute intern eine andere Referenz oder auch einen anderen Namen bekommen. Die Methoden jedoch bleiben namentlich als Schnittstellt nach draußen, da ist also nichts zu ändern.

                    Warum sollten sich ausgerechnet nur bei Eigenschaften Änderungswünsche ergeben, bei Methoden aber nie? Mögliche Namensänderungen sind jedenfalls nicht der Grund, warum man die Innereien nicht veröffentlichen will. Es geht im Wesentlichen darum, zusätzliche Funktionalität beim lesenden und schreibenden Zugriff hinzufügen zu können, ohne dass die Verwendungsstellen angepasst werden müssen. Vielleicht mag man aufgrund nicht vorhandener syntaktischer Möglichkeiten in bestimmten Sprachen Eigenschaften nicht veröffentlichen, weil beim Hinzufügen von Zugriffsfunktionalität auf eine Methode ausgewichen werden muss. In C# (und VB ab .NET) würde vermutlich keiner ernsthaft aus diesem Grund öffentliche Eigenschaften meiden, denn da hat man die Möglichkeit, gekapselten Zugriff transparent zu integrieren (Java-C#-Vergleich).

                    Außerdem hab ich da mal einen Artikel über "method chain" gelesen der mich aufhorchen hat lassen.
                    Nicht machen. Scroll mal nach oben... im Prinzip werden Interna damit nach draußen gereicht und das ist schlecht.

                    Nein, dabei wird nichts verwendet, was nicht sowieso schon public ist. Mich deucht, du hast Method Chaining nicht verstanden. Das ist mehr oder weniger nur eine komfortable Erweiterung der normalen benötigten Funktionalität.

                    Statt

                    $element = new HtmlElement();
                      $element->attribut('foo', 'bar');
                      $element->attribut('baz', 'qux');
                      $element->content('blafasel');
                      $html = $element->render();

                    kann man beim Method-Chaining schreiben:

                    $element = new HtmlElement();
                      $html = $element->attribut('foo', 'bar')->attribut('baz', 'qux')->content('blafasel')->render();

                    (oder auch mit Zeilenumbruch vor den ->) Ähnlich komfortabel ($element nicht dauernd wiederholen zu müssen) ginge das mit with (wenn es zur Verfügung stünde (und nicht solche Nebenwirkungen wie in Javascript hat))

                    $element = new HtmlElement();
                      with ($element) {
                        ->attribut('foo', 'bar');
                        ->attribut('baz', 'qux');
                        ->content('blafasel');
                        $html = ->render();
                      }

                    Ob die letzte Zeile wirklich so geht oder nur Eigenschaften und rückgabelose Methoden mit with verwendet werden können, sei mal dahingestellt.

                    dedlfix.

            2. Moin!

              Aber mal zur Sache.

              :)

              Ein einziges Singleton reicht schon, damit das Leben nicht mehr so schön ist.
              Gutes Argument! Mich verwirrt ein wenig dass du strikt dagegen bist später aber:

              Ich bin strikt dagegen. Singletons sind nicht vernünftig testbar. Sie erzeugen fix codierte Abhängigkeiten und Global State.

              Ich bin sehr dafür, Konfigurationsdaten und Programm strikt zu trennen. Ob nun als Singleton oder sonstwie gelöst: Diese Klasse liest einmal die Konfiguration ein und liefert die darin definierten Werte aus. Fertig.

              Das ich hier Singletons erwähne, liegt lediglich daran, dass andere sie so gern verwenden und sich auch angesprochen fühlen sollen.

              Am Ende hab ich 100 Zeilen die einfach funktionieren. Was sagst du, wenn ich noch eine Importdatei bräuchte müsste ich alles nochmal machen. Recht hast du.

              Ich hab absolut nichts dagegen, dass man Wegwerfcode quick und dirty schreibt, benutzt, und dann wegwirft.

              Da gibts keine Notwendigkeit für automatisierte Tests (das macht man interaktiv für nur den konkreten Fall manuell), es gibt keine Wartungsprobleme bei langfristiger Nutzung, keine Migration und Anpassung an sich verändernde Anforderungen.

              Das funktioniert aber nur, wenn man den Code wirklich wegwirft. ;)

              Ein Defaultwert ist für mich essenziell wichtig. Der Idealfall ist, dass ein Programm immer weiß was der Anwender machen möchte.

              Wenn du vom Anwender sprichst, dann meinst du den Programmierer? Denn das ist der Anwender deiner Klasse mit dem Defaultwert.

              Das dies nicht realisierbar ist, liegt auf der Hand, es sollte jedoch ein Anstreben sein. Star Trek Fans werden das Wort "Notenergie" wahrscheinlich zugenüge gehört haben. Ein Defaultwert ist der Zustand (Notenergie) in den etwas fällt (z.B. ein Objekt), wenn alle Stricke reißen.

              Wenn ich Programmierer und also Anwender deiner Klasse wäre, dann würde ich von der Klasse im Prinzip nur dein Interface sehen. Und das Interface sagt mir dann, dass ich einen Wert angeben _kann_ für die Cookie-Lebensdauer.

              Nun kennt man es ja vom Setzen von Cookies, dass man die Cookies persistent macht, wenn man eine Lebensdauer angibt, und dass es nur Session-Cookies sind, wenn man nichts angibt. Folglich würde ich beim Anblick des Interfaces vermuten: Die Lebensdauer triggert die Persistenz des Cookies, lasse ich die Angabe weg, gibts Session-Cookies.

              Und deine Klasse enttäuscht mich, indem sie den für mich willkürlichen Wert von 14 Tagen einsetzt. Warum 14 Tage? Kann man das irgendwo nachlesen? In der Klasse steht es jedenfalls nicht, denn dort ist, wie du sagst, nur der Aufruf des Singletons zur Besorgung des Default-Wertes. Also etwas potentiell konfigurierbares.

              Nun ja, ich bin also in die 14-Tage-Falle getappt, und korrigiere meinen Fehler. Ich will explizit 21 Tage, also setze ich die 21 Tage explizit in den Aufruf.

              Andere Entwickler kommen und gehen, und irgendwann entscheidet jemand, den Default-Wert auch auf 21 Tage zu setzen. Das berührt natürlich erstmal die Frage: Woher weiß man, welche Stellen das alles berührt? Aber darum solls eigentlich nicht gehen.

              Denn es vergeht noch etwas mehr Zeit, und wieder einmal wird die Cookie-Lebensdauer von 21 Tagen als falsch empfunden und soll überall reduziert werden auf 7 Tage... was natürlich dort scheitert, wo ich explizit 21 Tage konfiguriert hatte. Hmpf... ;)

              Ich gebe gern zu: Dieses Beispiel ist eigentlich eher eine Anwendung von Konfigurationsparametermanagement. Die Frage, die ich deshalb eigentlich stellen sollte: Wieso erlaubt mir deine Klasse das Wohlgefühl des Rundum-Glücklich-Pakets, obwohl im Sinne der Applikation und der Transparentmachung der nötigen Konfigurationen eigentlich zu fordern wäre, dass ich meine 21 oder 14 oder sonstwie besonderen Tage Cookielebensdauer viel besser ganz bis in die Konfigurationsdatei der Applikation getragen hätte. Die Konfiguration hätte dann festlegen können, dass tatsächlich meine individuellen 21 Tage gelten sollen, oder dass man in der Konfiguration umstellt auf den globalen Lebensdauerwert von 14 Tagen, den man dann ändert auf 21 Tage, und später auf 7.

              Die Klarheit in der Anwendung der Klasse ist für mich größer, wenn mich die Klasse zwingt, explizit einen Wert anzugeben, und wenn ich diesen Wert auf sehr einfache Weise über die Konfiguration dorthin bekomme.

              Das bedeutet für mich:

              • Es gibt keinen Default-Wert, der auf magische Weise schon mal vorbelegt wird.
              • Die Klasse fordert explizit alle ihre Abhängigkeiten (und die Konfiguration der gewünschten Cookie-Lebensdauer betrachte ich jetzt mal als so eine - du darfst dir aber gerne auch andere Parameter vorstellen, wie URLs, Dateinamen, etc.) vom Programmierer ein, und dieser muss liefern.
              • Die Lieferung ist auf einfache Weise möglich, indem man das Konfigurationsobjekt auf einfache Weise an der Stelle verfügbar hat, wo man es benötigt, und in dem es auf einfache Weise möglich ist, den gewünschten Wert entweder individuell festzulegen, oder den Default-Wert zu übernehmen.
              • Die Übernahme des Default-Wertes anstelle eines individuellen ist Aufgabe des Konfigurationsobjekts.

              Vorteil: Weniger Testaufwand, weil es nur eine gültige Art des Aufrufs gibt, nämlich mit explizit angegebenem Parameter. Und den kann ich im Test variieren.

              Nachteil der Singleton-basierten Injektion des Default-Wertes: Wie soll ich zum Test dort unterschiedliche Werte hineinbekommen?

              Vorteil:

              • etwas funktioniert mit möglichst geringer Konfiguration

              Würde ich nicht so sehen. Im Gegenteil muss sich die Klasse nicht nur mit sich selbst beschäftigen, sondern auch noch mit der Beschaffung des Default-Wertes.

              • weniger Exception Behandlungen

              Exceptions sind bislang nirgends im Spiel gewesen. Im Vergleich zwischen magischen Default-Werten und expliziter Werteübergabe sehe ich auch nicht, warum da irgendwo eine Exception vorkommen muss.

              • wahrscheinlich einfachere Aufrufe (in deinem Fall müssen ja immer alle Parameter bekannt sein)

              Die Parameter stehen sowieso alle in der Funktionssignatur. Und das Konfigurationsobjekt herzubekommen, um die konfigurierte Lebensdauer oder sonst einen Wert hineinzutun, ist auch nicht schwer.

              Nachteil:

              • Objekte laufen mit Defaultwerten, auch wenn man das nicht möchte...

              Objekte haben zwei Gesichter, obwohl sie nur eines haben sollten: Mit explizitem Wert, oder mit Default.

              Außerdem: Objekte sind hart verdrahtet abhängig von dem Konfigurations-Singleton, obwohl man eigentlich nur einen Integer übergeben müsste.

              Die Cookie Klasse würde in Auszügen bei mir so aussehen

              class cCookie
              {
                 $intCookieLifetimeDays = cSystem::init()->getCookieLifetime();
                 public function getCookieLifetime()
                 {
                    return $this->intCookieLifetimeDays;
                 }

              public function setCookieLifetime($intCookieLifetimeDays)
                 {
                     $this->intCookieLifetimeDays = $intCookieLifetimeDays
                 }
              }

              
              >   
              > Der Defaultwert wird anfangs von meinem Gott-Singleton in das Objekt gesetzt. Das wird im Konstruktor erledigt. Danach hat das Objekt einen Defaultwert. Der kann jederzeit durch die Setter Methode angepasst werden.  
                
              Ich würde mir sowohl den Singleton-Aufruf im Konstruktor, wie auch die Getter und Setter für diesen Wert komplett sparen, und stattdessen im Konstruktor einfach nur den gewünschten Wert übergeben.  
                
              Das spart mir zwei Methoden in der Klasse (Objekte haben sowieso meist zuviele davon), ich habe keine hartcodierte Abhängigkeit vom Singleton, und die Funktionsweise der Klasse wird in ihrem Interface deutlicher: "Wenn du mich benutzen willst, brauche ich die gewünschte Lebensdauer der Cookies in Tagen."  
                
              An der Stelle, an der man die Klasse dann wirklich braucht:  
                
              `$cookieSetter = new cCookie(cSystem::init()->getCookieLifetime());`{:.language-php}  
                
              Ja, da ist dein Singleton noch drin. Das kann ich nicht so einfach rauswerfen, weil dein gesamtes System darauf basiert. Ich findes es, jetzt wo ich es genauer betrachte, übrigens interessant: Du setzt da, jedenfalls laut dem Funktionsnamen, eben gerade KEINEN Default-Wert für die Cookie-Lebensdauer, denn sonst hieße die Funktion sowas wie "getDefaultCookieLifetime".  
                
              Weil sie aber nun mal nicht so heißt, ist das eigentlich schon der beste Beweis dafür, dass es in deinem System sowas wie Default-Konfigurationswerte nicht gibt. Es gibt nur Konfigurationswerte.  
                
              Jeder Default-Konfigurationswert, der verwendet werden soll, erfordert eine Methode im Singleton (und mutmaßlich irgendwie einen Eintrag in einer Konfigurationsdatei). Wann immer eine Klasse einen neuen Default-Wert braucht, muss das Singleton angepasst werden, damit die Verwendung der Klasse dann ohne das Singleton auskommen kann - oder nochmal einen Konfigurationswert erfordert, der ebenfalls aus dem Singleton kommt.  
                
              Sieht für mich irgendwie nicht wirklich nach Arbeitserleichterung aus. :) Mal abgesehen von der beständigen Verwendung des Singletons.  
                
              
              > Wenn du jetzt irgendwann den Defaultwert ändern möchtest, darfst du das an x Stellen machen. Außer natürlich du kapselst den Aufruf in irgendeiner Form...also Depency Injection. Das hast du ja als erstes Vorgeschlagen.  
                
              `new cCookie(14);`{:.language-php} ist Dependency Injection. Die externe Abhängigkeit ist der zu benutzende Wert für die Cookie-Gültigkeit, ohne den die Klasse nicht arbeiten kann.  
                
              
              > Man könnte natürlich das ganze auch ohne D.I. erledigen:  
              > ~~~php
                
              
              > new cCookie(getCookieLifetime());  
              > 
              
              

              Das ist AUCH Dependency Injection. Die Dependency der Klasse ist die Lebensdauer des Cookies.

              Dann hat man jedoch wieder das Problem des Gott-Singletons bzw. verstreuen sich dann die Konfigurationselemente vielleicht im gesamten Sourcecode.

              Ich bin ja gegen Singletons, erst recht die von Karel Gott. ;)

              OOP hin, Martin Fowler her, du kannst es nennen wie du willst, aber ich stehe auf mein Gott-Singleton. Und wenn mein Chef irgendwann mal möchte, dass es anders heißt, dann werde ich deinen Chef dazu anstiften dein Depency Injektion Verwaltungsobjekt anders zu benennen.

              Man gut, dass mein Chef darauf keinen Einfluss hat. ;)

              - Sven Rautenberg

              1. Ich bin auch froh das mein Chef keinen Einfluss auf die Programmierung hat :).

                Du kannst es nennen wie du willst, aber auch in deinem System gibt es Defaultwerte. Wenn du 10 mal den Aufruf new cCookie(14) in deinem System hast, dann mutiert die 14 zu einem Defaultwert. Bloß musst du eben 10 stellen in deinem System ändern, um aus den 14 ein 21 zu machen.

                Außerdem ist dieser Aufruf new cCookie(14) für mich eine simple Objekt Initialisierung mit Parameter Übergabe. Eine Dependency Injection muss für mich aus einem gesonderten Bereich bestehen, der sich primär um den Controllfluss kümmert. Also sowas:

                  
                function di()  
                {  
                   newcObject( new cOtherObject() );  
                }  
                
                

                Generell stell ich mir die Frage wieso einfachste Dinge mit irre komplizierten Namen versehen werden. Das Problem dabei ist, dass viele meinen Sie haben Ahnung nur weil sie ein paar englische Fachwörter kennen. Ich hab z.B. keine Ahnung was es so alles in einem Auto gibt oder gar wie es funktioniert. Trotzdem kann ich Autofahren.
                Das gleiche gilt für die Benennung von Methoden. Nur weil in der Methode für die Lebensdauer kein "Default" vorkommt heißt das noch lange nicht, dass das eine Wert fürs Kuchenbacken ist. Wir dürfen nicht den gleichen Fehler machen wie die Politik und die Sprache Zweckentfremden. Sprache ist zum transportieren von Informationen da und nicht zum täuschen.

                Das nach außen nicht direkt der Defaultwert ersichtlich wird stimmt. Da hast du auf jeden Fall recht (und ehrlich gesagt nervt mich das selber, auch die Tatsache das man in php über ein Interface nicht den Typ des Rückgabeparameter bestimmen kann). Du behebst diesen Markel in dem du 10 Aufrufe mit dem Übergabewert 14 hast und somit 10 Stellen an denen du eventuell etwas ändern musst. Ich setze dieses Wissen voraus oder muss es dokumentieren (wie dedlfix es bereits erwähnte). Ersteres bricht die Regeln der OOP. Gut ich würde jetzt sagen bei einem Default wert macht das nichts. Dann kannst du aber sagen dass ein System nicht nur einen Default wert hat und damit hättest du wiederrecht. Und schwups sind wir beim Problem der ersten Frage angelangt und bei meiner Neugier. Ich akzeptiere diesen Markel, dass man eben gegen OOP verstößt. Dafür zentralisiere ich Defaultwerte in einem Singleton und erhoffe mir dadurch Arbeitserleichterung. Und deshalb habe ich mich hier überhaupt zu Wort gemeldet. Dein Ansatz hat mich sehr interessiert, doch konntest du mich bis jetzt nicht überzeugen.

                Und nein Objekte haben keine zwei Gesichter sondern x. Wenn du nur eine Eigenschaft hast, welche die Werte zwischen 0 - 99 annehmen kann, hast du schon 100 unterschiedliche mögliche Objekte.

                Gruß
                OOP (origineller Ohrwurm Produzent)
                T-Rex

                1. Moin!

                  Du kannst es nennen wie du willst, aber auch in deinem System gibt es Defaultwerte. Wenn du 10 mal den Aufruf new cCookie(14) in deinem System hast, dann mutiert die 14 zu einem Defaultwert. Bloß musst du eben 10 stellen in deinem System ändern, um aus den 14 ein 21 zu machen.

                  Es ist ein Unterschied, ob ich bewußt den aus der Konfiguration heraus verfügbaren Wert für die überall anders ebenfalls verwendeten 14 Tage verwende, oder ob die Klasse aus obskuren (d.h. von außen unsichtbaren) Quellen auf diesen Wert kommt.

                  Ich bin gegen solche Default-Magie aus den dargelegten Gründen. Sinnvolle Defaults zu verwenden, um dem Programmierer das Leben leichter zu machen, hat seinen Anwendungsfall dann, wenn man für eine größere Öffentlichkeit Frameworks programmiert. Von solch einem Fall war hier bislang nicht die Rede.

                  Der Standardfall für Programmieraufgaben ist die Lösung eines konkreten Problems. Das Verwenden der Cookie-Klasse hat also einen konkreten Anwendungsfall mit einer konkret definierbaren erwünschten Lebensdauer. Diese Lebensdauer ist von einem konkreten Konfigurationswert abrufbar und kann direkt in die Klasse hineingegeben werden, ohne Magie. Der Programmierer muss sich an diesem Punkt dann in der Tat die Frage stellen, welche Lebensdauer für das Cookie vorgesehen sein soll. Diese Frage wird entweder reflexartig mit "Die Lebensdauer, die für alle Cookies gilt" beantwortet werden können, und deshalb automatisch den Aufruf der zugehörigen Konfigurationsfunktion hervorbringen, oder es ist ein abweichender Wert mit den daran hängenden Konsequenzen, nämlich der Schaffung eines neuen Konfigurationswertes.

                  Außerdem ist dieser Aufruf new cCookie(14) für mich eine simple Objekt Initialisierung mit Parameter Übergabe.

                  Alles, was das korrekte Funktionieren (im Sinne der Aufgabenstellung) eines Objektes beeinflusst, aber potentiell flexibel und dynamisch sein muss, definiere ich als Abhängigkeit (Dependency) des Objekts. Im Sinne des Prinzips der "Inversion of Control" gehört diese Dependency dem Objekt von außen übergeben, anstatt dass sich das Objekt diese Dependency selbst besorgt.

                  Insofern betrachte ich auch simple Konfigurationsparameter als Dependency, die deshalb folgerichtig zu injecten sind. :)

                  Eine Dependency Injection muss für mich aus einem gesonderten Bereich bestehen, der sich primär um den Controllfluss kümmert. Also sowas:

                  function di()
                  {
                     newcObject( new cOtherObject() );
                  }

                    
                  Täte es sehr weh, wenn du in genau so einem Bereich folgende Funktion notiertest:  
                    
                  ~~~php
                  function getCookieClass()  
                  {  
                      return new cCookie($this->configValue("CookieLifeTime"));  
                  }
                  

                  Generell stell ich mir die Frage wieso einfachste Dinge mit irre komplizierten Namen versehen werden.

                  Ich würde dir ja sehr gerne zeigen, wohin es führt, wenn man glaubt, dass man es mit einfachsten Dingen zu tun hat und deshalb die direkteste Programmiermethode verwendet, leider sollte ich wohl allzu direkte Veröffentlichungen lieber nicht tätigen.

                  Deshalb musst du mir leider glauben, dass Projekte, sofern sie eine gewisse Größe und Komplexität überschreiten, auf diese Weise immer aufwendiger zu warten sind, bis am Ende sich keiner mehr traut, auch nur irgendwas zu verändern, und man solch ein Knäuel von Code im Prinzip nur noch wegwerfen kann.

                  Es kann ja durchaus sein, dass dein Projekt so klein und übersichtlich ist, dass dein Ansatz funktioniert. Ich bestreite nicht, dass es funktioniert, weder grundsätzlich, noch konkret beim Ausführen des Codes an sich. Ich habe nur so meine Erfahrungen gesammelt im Laufe der Zeit, welche Ansätze auf Dauer nicht funktionieren, und welche besser sind. Und meine persönlichen Meinungen stimmen mit der restlichen Fachwelt erstaunlich gut überein.

                  Das Problem dabei ist, dass viele meinen Sie haben Ahnung nur weil sie ein paar englische Fachwörter kennen. Ich hab z.B. keine Ahnung was es so alles in einem Auto gibt oder gar wie es funktioniert. Trotzdem kann ich Autofahren.

                  Du redest gerade mit einem Automechatroniker - um im Bild zu bleiben.

                  Das gleiche gilt für die Benennung von Methoden. Nur weil in der Methode für die Lebensdauer kein "Default" vorkommt heißt das noch lange nicht, dass das eine Wert fürs Kuchenbacken ist.

                  Über die Wahl von Variablen- und Methodennamen kannst du beim Leser deines Codes recht gut steuern, ob er den Code versteht, oder nicht.

                  Nur mal ein Beispiel:

                  function checkValidation($authdata1, $authdata2)  
                  {  
                      if (empty($authdata1) || empty($authdata2)) {  
                          return 14;  
                      }  
                      if (strlen($authdata1) != 5) {  
                          return 23;  
                      }  
                      if (preg_match("/\\./", $authdata2)) {  
                          $data = explode(".", $authdata2);  
                          //...  
                  }
                  

                  Anhand der Variablennamen kannst du in diesem Stückchen Code nicht erkennen, warum auf Stringlänge 5 oder auf den Punkt getestet wird.

                  Und nun ersetze mal $authdata1 durch "$postleitzahl" und $authdata2 durch "$geburtsdatum".

                  Wir dürfen nicht den gleichen Fehler machen wie die Politik und die Sprache Zweckentfremden. Sprache ist zum transportieren von Informationen da und nicht zum täuschen.

                  Programmierer machen aber leider oft den Fehler, durch falsche Sprache andere Programmierer und vor allem auch sich selbst zu verwirren.

                  Das nach außen nicht direkt der Defaultwert ersichtlich wird stimmt. Da hast du auf jeden Fall recht (und ehrlich gesagt nervt mich das selber, auch die Tatsache das man in php über ein Interface nicht den Typ des Rückgabeparameter bestimmen kann). Du behebst diesen Markel in dem du 10 Aufrufe mit dem Übergabewert 14 hast und somit 10 Stellen an denen du eventuell etwas ändern musst.

                  Ich habe 10 Stellen, an denen ich den in der Konfiguration einzigen Cookie-Lifetime-Wert explizit in die Klasse übergebe und damit dokumentiere, welche Konfigurationsparameter an diesem Punkt in dem nutzenden Code aktiv werden. Alternativ nutze ich explizit einen anderen, für den speziellen Zweck geschaffenen Konfigurationsparameter. Das System verlangt vom Programmierer immer, dass er sich Gedanken machen muss über den an dieser Stelle korrekt zu verwendenden Konfigurationsparameter, weil er andernfalls an dieser Stelle nicht weiterprogrammieren kann, weil die Klasse dann nicht läuft.

                  Das bedeutet auch, dass an dieser Stelle die Aufgabe, konkret jetzt also das Cookiesetzen, vollständig verstanden wurde, und deshalb diese Entscheidung vom Programmierer getroffen werden kann. Eine einstweilen nur mit Defaults befüllte Klasse, Hauptsache sie funktioniert irgendwie, hat in meinen Augen das Potential für sehr gut versteckte Bugs (und ich gebe gern zu, dass es bei einer Cookie-Lebensdauer extrem schwer wäre, das wirklich argumentativ zu belegen), oder für die wachsende Angst der Programmierer, dass beim Ändern dieser konfigurierten Default-Werte tatsächlich alles wir gewünscht anders, aber immer noch korrekt funktioniert. Stattdessen wird man vermutlich irgendwann dazu übergehen, den Default-Werten in der Klasse zu misstrauen und immer explizit eigene Werte anzugeben, damit wenigstens dieser Codeteil beim Ändern des Defaultwerts nicht kaputt geht.

                  Ich setze dieses Wissen voraus oder muss es dokumentieren (wie dedlfix es bereits erwähnte). Ersteres bricht die Regeln der OOP. Gut ich würde jetzt sagen bei einem Default wert macht das nichts. Dann kannst du aber sagen dass ein System nicht nur einen Default wert hat und damit hättest du wiederrecht. Und schwups sind wir beim Problem der ersten Frage angelangt und bei meiner Neugier. Ich akzeptiere diesen Markel, dass man eben gegen OOP verstößt. Dafür zentralisiere ich Defaultwerte in einem Singleton und erhoffe mir dadurch Arbeitserleichterung. Und deshalb habe ich mich hier überhaupt zu Wort gemeldet. Dein Ansatz hat mich sehr interessiert, doch konntest du mich bis jetzt nicht überzeugen.

                  Ich weiß. Die Frage an dieser Stelle: Schreibst du Unit-Tests mit PHPUnit? Und schreibst du die VOR oder NACH der Erstellung deiner Klasse?

                  Wenn du keine Unit-Tests schreibst, und auch noch nicht erfahren hast, auf welche wunderbare Weise dir Unit-Tests den Arsch retten können, weil testbarer und getesteter Code einfach eine viel höhere Softwarequalität liefert, so dass man tatsächlich termingerecht liefern kann, anstatt in einer endlosen QA-Phase immer neue Fehler zu finden, dann verstehe ich deine Einstellung vollkommen.

                  Und vermutlich wirst du extrem fluchen, wenn du in dem jetzigen Zustand auch nur den ersten Unit-Test schreiben solltest, weil du deine Klassen nicht isoliert testen kannst, weil sie nicht isolierbar sind, sondern z.B. durch das Konfigurations-Singleton immer zwingend auf genau dieses zugreifen.

                  Und nein Objekte haben keine zwei Gesichter sondern x. Wenn du nur eine Eigenschaft hast, welche die Werte zwischen 0 - 99 annehmen kann, hast du schon 100 unterschiedliche mögliche Objekte.

                  So ein Objekt hätte für mich vier oder fünf Testfälle: -1, 0, 99 und 100, vielleicht noch was aus der Mitte: 42.

                  Es hätte aber trotzdem nur ein Gesicht. Nämlich das mit 100% Code-Coverage durch Tests. ;)

                  - Sven Rautenberg

                  1. Also ehrlich du könntest es in der Politik weit bringen. Am Ende wird sich in Details und Klugscheisserei geflüchtet, hauptsache man muss keine Einsicht mitbringen, sehr schade und eine fruchtbare Diskussion ist das nicht. Das ist Rechthaberei und Arroganz vom feinsten.

                    Ich sage, dass du aufgrund eines Namens nicht auf die Funktionalität schliessen solltest, zumal das hier ein Forum ist und ich mir die Beispiele aus den Fingern sauge. Als Reaktion darf ich mir einen Vortrag von sprechenden Variablennamen anhören - lächerlich.
                    Und ja mein System steht "fast" komplett auf Unittests. Und siehe da Unittests sind kein Problem. Da staunt die Fachwelt oder?

                    Also es ist anscheinend ein Unterschied ob man für eine große Öffentlichkeit programmiert oder für sich selbst. Öffentliche Sachen müssen anscheinend leicht zu bedienen sein, während interne Sachen mit Parametern zugepumpt werden. Oder welchen Unterschied kann uns da die Fachwelt erklären?

                    Achja Magie ist das nicht, dass nennt man dann eine Blackbox.

                      
                    function getCookieClass()  
                    {  
                        return new cCookie($this->configValue("CookieLifeTime"));  
                    }  
                    
                    

                    Für jeden Defaultwert also eine eigene Funktion. Das ist doch mal ein Ansatz den man verfolgen sollte. Das ist bestimmt übersichtlich!

                    Aber um das alles mal aufs eigentliche Thema zurück zu führen. Die Frage war ja wie man am besten Konfigurationssachen verwaltet. Meine Methode ist ein (Gott-)Singleton zu verwenden. Deine Methode ist eine Konfigurationsdatei in form einer ini oder XML Datei? Aber wie kommen die Werte aus diesen Dateien ins PHP? Und da gibt es ja nur zwei Ansätze. Entweder auch ein zentraler Bereich, also eine Art Gott-Singleton oder sie stehen verstreut im Code. Mich würde brennend interessieren was die "Fachwelt" dazu sagt!

                    viel Spaß beim Zitieren

                    setGruss("T-Rex");

                  2. Im Gegenteil zu T-Rex möchte ich Dir für Deine ausführlichen und für mich teilweise inspieriend aufschlussreichen Ausführungen danken.

                    Zwei Fragen hätte ich:

                    1. In einem anderen Posting hast Du den Erfahrungswert erwähnt, dass meist zu viele Methoden in einer Klasse wohnen. Darf ich das pauschal auf die Lösung mehr Klassen mit weniger Methoden münzen?

                    2. Gehen wir mal von einer hinreichend komplexen JavaScript-GUI aus. Die Situation scheint mir hier wg. der Benutzerinteraktion / Events / Browserunterschieden zunächst schwieriger. Zentrale Bestandteile lassen sich dann nur über Fernsteuerungen wie z.B. Selenium automatisiert testen?

                      1. In einem anderen Posting hast Du den Erfahrungswert erwähnt, dass meist zu viele Methoden in einer Klasse wohnen. Darf ich das pauschal auf die Lösung mehr Klassen mit weniger Methoden münzen?

                      Mal von Getter und Setter Methoden abgesehen sollte eine Klasse an die 7 Methoden haben. Eine Methode wiederum sollte an die 4 Parameter haben.

                      Gerade wenn ein Projekt wächst ist man leicht dazu geneigt seine Klassen explodieren zu lassen. Früher oder später wird man jedoch mit Wartbeikeitsproblemen zu kämpfen haben. Um dem entgegen zu wirken gibt es für diverse Fälle eben Software Patterns. Eine davon ist die von Sven vorgestellte Dependency Injektion.

                      1. Gehen wir mal von einer hinreichend komplexen JavaScript-GUI aus. Die Situation scheint mir hier wg. der Benutzerinteraktion / Events / Browserunterschieden zunächst schwieriger. Zentrale Bestandteile lassen sich dann nur über Fernsteuerungen wie z.B. Selenium automatisiert testen?

                      Ob Javascript, PHP, C++ oder Java das vorgehen ist immer das selbe. Durch die OOP zerlegst du dein System in kleine kompakte und wenn möglich in sich abgeschlossene Häppchen. Diese kannst du dann bequem mit diversen Unittests testen. Im Falle einer Browserweiche würde das ganze vielleicht so aussehen:

                        
                      if(IE)  
                      {  
                         objObjekt.method(IEstuff);  
                      } else {  
                         objObjekt.method(OtherStuff);  
                      }  
                      
                      

                      IEtuff und OtherStuff sollten den gleichen Wert liefern. Dass könnte z.b. die Mauszeigerposition sein (da haben IE und z.b. FF unterschiedliche Methoden die aus zu lesen?). In die Methode wird aber nur der Wert an sich übergeben. In der "method" darf dann keine Browserweiche mehr vorkommen, dann kann man diesen Part unabhängig vom Browser testen.
                      Denkbar wäre hier die von Sven bevorzugte Dependency Injektion:

                        
                      if(IE)  
                      {  
                         objObjekt.method(objObjectIE);  
                      } else {  
                         objObjekt.method(objObjectFF);  
                      }  
                      
                      

                      die Method selbst würde dann so aussehen:

                        
                      method(objObject)  
                      {  
                      var something = objObject.getSomething();  
                      //--- do something  
                      }  
                      
                      

                      Du testest erst ob bei den Objekten objObjectIE und bei objObjectFF in beiden Fällen der gleiche wert aus der getSomething Methode kommt (eventuell auch per Unittest). Dann kannst du bei den Unittests dich auf ein Objekt versteifen.

                      Falls du das "if" an mehreren stellen brauchst, kannst du es wiederum in eine Funktion oder in eine Methode packen.

                      Wie man sieht hat Dependency Injection seine Berechtigung. Auf das Thema DI und Defaultwerte gehe ich jedoch nicht nochmal ein ;).

                      Gruß
                      Guy Fawkes lächelnder
                      T-Rex

                      1. Hi.

                        Mal von Getter und Setter Methoden abgesehen sollte eine Klasse an die 7 Methoden haben. Eine Methode wiederum sollte an die 4 Parameter haben.

                        Wie kommst Du auf diese Zahlen?

                        Ob Javascript, PHP, C++ oder Java das vorgehen ist immer das selbe.

                        Bei PHP, C++ oder Java kann man sich für gewöhnlich beim Bestehen der Tests auf einem System entspannen, das ist ein großer Unterschied zu JavaScript. Schau Dir z.B. mal an, was jQuery mit Test Swarm hier für einen immensen Aufwand betreibt.

                        Durch die OOP zerlegst du dein System in kleine kompakte und wenn möglich in sich abgeschlossene Häppchen.

                        Das ist sehr allgemein, aber ja, einverstanden.

                        Im Falle einer Browserweiche würde das ganze vielleicht so aussehen...

                        Mit JavaScript hast Du wenig am Hut, oder? Browserweichen sind eine ganz schlechte Idee, das Stichwort lautet Feature Detection

                        1. Moin!

                          Mal von Getter und Setter Methoden abgesehen sollte eine Klasse an die 7 Methoden haben. Eine Methode wiederum sollte an die 4 Parameter haben.

                          Wie kommst Du auf diese Zahlen?

                          Warum 7 als Zahl der Methoden ein anzustrebender Wert sein soll, entzieht sich meiner Kenntnis. Natürlich wäre es doof, wenn man fünf Klassen mit je einer Methode hat, wenn man auch fünf Methoden in einer Klasse haben könnte. Das kann aber keine grundsätzliche Regel sein, es hängt vielmehr von dem ab, was man damit erreichen will.

                          Die Anzahl von 4 Parametern pro Methode würde ich hingegen als absolut nicht empfehlenswert bezeichnen.

                          Eine gute Methode hat null Parameter.

                          Unumgänglich ist meist ein Parameter.

                          Bei zwei Parametern wird's schon kritisch, denn die Zahl der Testfälle steigt: Jeder mögliche Wert für den ersten Parameter kann mit jedem möglichen Wert für den zweiten Parameter kombiniert auftreten und muss getestet sein. Wobei "möglicher Wert" natürlich zusammenfassbar ist: Ein Username zerfällt vermutlich in die Bereiche "Leerstring bzw. zu kurz", "korrekt", "zu lang" und "ungültige Zeichen" - also vier Testfälle. Kombinier das mit einem zweiten Parameter, der auch "nur" vier Fälle hat, dann muss die Funktion mit zwei Parametern schon Tests für 16 Fälle kriegen.

                          Drei Parameter sind dementsprechend schon gar nicht mehr schön.

                          Ob Javascript, PHP, C++ oder Java das vorgehen ist immer das selbe.

                          Bei PHP, C++ oder Java kann man sich für gewöhnlich beim Bestehen der Tests auf einem System entspannen, das ist ein großer Unterschied zu JavaScript. Schau Dir z.B. mal an, was jQuery mit Test Swarm hier für einen immensen Aufwand betreibt.

                          Es gibt auch Unit-Test-Frameworks für Javascript. Aber da bin ich kein Experte.

                          Im Falle einer Browserweiche würde das ganze vielleicht so aussehen...

                          Mit JavaScript hast Du wenig am Hut, oder? Browserweichen sind eine ganz schlechte Idee, das Stichwort lautet Feature Detection

                          Abgesehen davon hat T-Rex die Dependency Injection, die ich beschrieb, nicht korrekt in Javascript umgesetzt. Ich würde mir allerdings auch nicht anmaßen, zu OOP mit Javascript kritische Beiträge zu schreiben, ich kenne Javascript dazu nicht gut genug, um die mit dieser Sprache vernünftigen Ansätze anwenden zu können.

                          Mir würde aber auch nicht einfallen, warum man den Code von T-Rex so in PHP programmieren wollen würde, selbst wenn's im Browser ginge.

                          - Sven Rautenberg

                        2. Wie kommst Du auf diese Zahlen?

                          Die Zahlen sind lediglich Richtwerte die ich immer wieder in Büchern gelesen hab. Außerdem kann ich aus eigener Erfahrung sprechen das 7 Methoden das maximale ist was man noch warten kann. Erhält die Klasse mehr Methoden wird es zunehmend unübersichtlicher. Aber wie gesagt es sind nur Richtwerte.

                          Auch die Zahl mit Parametern ist nur ein Richtwert. Im Idealfall hat eine Methode keine Parameter. 4 ist das maximum. Ich denke jeder hatte schon Methoden oder funktionen mit > 7 Parametern. In der Methode selbst ist alles mit if vollgepumpt und es gibt massive Probleme beim testen. Außerdem sind solche Fälle auch ein Indiz für ein fehlerhaftes OOP Design. Aber auch hier gibt es natürlich Ausnahmen.

                          Den Fall den Sven angesprochen hat ist dagegen wieder ein Sonderfall. Wenn du zwei Parameter mit jeweils 4 Testfällen hast, hast du nur 16 Testfälle wenn die Parameter direkten Einfluss aufeinander haben. Wenn du in eine Methode z.B. einen Namen und einen Nachnamen gibts und beide haben jeweils 4 Testfälle, dann hast du 8 Testfälle. Den für die Behandlung des Nachnamen ist es wahrscheinlich unerheblich ob der Name leer ist oder Sonderzeichen enthält. Hast du jedoch z.B. eine Rechnung (parameterA * parameterB) welche nicht 0 sein darf, so haben beide Parameter direkten Einfluss aufeinander und du hast tatsächlich 16 Testfälle.

                          Ob Javascript, PHP, C++ oder Java das vorgehen ist immer das selbe.

                          Bei PHP, C++ oder Java kann man sich für gewöhnlich beim Bestehen der Tests auf einem System entspannen, das ist ein großer Unterschied zu JavaScript. Schau Dir z.B. mal an, was jQuery mit Test Swarm hier für einen immensen Aufwand betreibt.

                          Falsch. Bei Java und C++ muss man für Desktopanwendungen die verschiedenen Systeme auf denen das Ding laufen soll selbstverständlich auch testen. Allein um die verschiedenen Betriebsysteme unter kontrolle zu haben brauchst du schon verschiedene Tests. Aber genau deshalb greift man ja zur OOP. Es gibt eine Schicht die Regelt alle System spezifischen Fälle (sofern von der Programmiersprache nicht selbst übernommen) z.B. Arbeitsspeicher zugriff oder im Fall von Javascript eben Mauszeigerposition. Diese Schnittstellen bekommen "gesonderte" Unittests. Dann hast du pro System was du unterstützen willst einen extra Unittest. Für das eigentliche System brauchst du nur noch einen Unittest und musst es nur noch auf einem System testen.
                          Deshalb greifen ja viele zu JQuery oder YUI. Die bieten dir eine Funktion die Mauszeigerposition zu bekommen. Du musst dich nicht mehr um die verschiedenen Browser kümmern (zumindest nicht in dem Fall).

                          Im Falle einer Browserweiche würde das ganze vielleicht so aussehen...

                          Mit JavaScript hast Du wenig am Hut, oder? Browserweichen sind eine ganz schlechte Idee, das Stichwort lautet Feature Detection

                          Mein Stichwort lautet "Zweckentfremdung der Sprache". Nenn es wie du willst. Mach es wie du willst. Am Ende hast du eine BROWSERWEICHE!! Und nein Fachwörter entscheiden nicht über Kompetenzen, Kompetenz entscheidet über Kompetenz.

                          Gruß
                          Mensch mit Menschenverstand
                          T-Rex

                    1. Moin!

                      Im Gegenteil zu T-Rex möchte ich Dir für Deine ausführlichen und für mich teilweise inspieriend aufschlussreichen Ausführungen danken.

                      Zwei Fragen hätte ich:

                      1. In einem anderen Posting hast Du den Erfahrungswert erwähnt, dass meist zu viele Methoden in einer Klasse wohnen. Darf ich das pauschal auf die Lösung mehr Klassen mit weniger Methoden münzen?

                      Wenn eine Klasse viele Methoden hat, dann besteht zumindest der Verdacht, dass sie zuviele verschiedene Sachen tut. Es wäre dann zu untersuchen, ob diese Menge an Methoden gerechtfertigt ist, oder ob sich die Klasse nach thematischen bzw. funktionalen Aspekten aufteilen lässt.

                      Es gibt ein PHP-Tool, welches eine statische Codeanalyse vornimmt und anhand von (anpassbaren) Regeln gewisse Konstrukte anmeckert: PHP Mess Detector

                      Der Grenzwert für die Anzahl von Methoden einer Klasse liegt bei 10.

                      1. Gehen wir mal von einer hinreichend komplexen JavaScript-GUI aus. Die Situation scheint mir hier wg. der Benutzerinteraktion / Events / Browserunterschieden zunächst schwieriger. Zentrale Bestandteile lassen sich dann nur über Fernsteuerungen wie z.B. Selenium automatisiert testen?

                      Selenium setzt an der kompletten Applikation an und kann an dieser Stelle das korrekte Funktionieren testen. Solche Tests sind allerdings aufwendig zu pflegen, weil schon simple Veränderungen an der Oberfläche die Tests zum Scheitern bringen können. Das kann man über Abstraktionen etwas abpuffern, erzeugt sich damit allerdings wieder eine Schicht, deren korrektes Funktionieren man testen muss.

                      Unit-Tests testen im Idealfall nur exakt eine PHP-Klasse. Es ist allerdings schwierig, in ein bestehendes System nachträglich Unit-Tests zu integrieren, ohne dabei heftige Widerstände durch suboptimale Strukturen überwinden zu müssen. Das notwendige Setup für einen Test kann dabei durchaus so komplex werden, wie die gesamte Applikation - und das dann pro Klasse!

                      Wenn das Testen von Anbeginn berücksichtigt wird, hat man es viel einfacher. Und in der Regel programmiert man dann auch bessere Interfaces - und vor allem kann man schlechte Klassenstrukturen ohne große Bedenken, etwas unbemerkt kaputtzumachen, umbauen.

                      - Sven Rautenberg

                      1. Vor allem zu dem mit den Unittests kann ich nur eins dazu sagen:
                        Amen!
                        Unittests sind ein Segen und die eigene Programmierweise verbessert sich erheblich!

                        Gruß
                        Gottloser
                        T-Rex

  5. Hello,

    falls hier jemand überhaupt darauf achtet:
    Ich lese noch mit, finde es aber im Moment (aus anderen Gründen) zu anstrengend, auf die einzelnen Postings zu reagieren. Das ist aber vermutlich genau das Indiz dafür, dass ich sie auch verarbeiten sollte...

    Also bitte noch nicht aufhören mit dem Brainstorming :-)

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de