Ankündigung

Einklappen
Keine Ankündigung bisher.

DI-Container Key

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • DI-Container Key

    Hallo,

    ich bin mir derzeit unterschiedliche DI Container am anschauen und kann teilweise einige Sachen nicht nachvollziehen. Ganz besonders die Tatsache, dass man Services einfach nur einen namen gibt und mit dem Servicenamen arbeitet.

    Hat uns die OOProgrammierung dafür nicht interfaces geschenkt?

    Beispiel:

    $di->get('service.logger');

    hier wird anhand des strings service.logger der logger geholt. dabei ist völlig unklar was der logger für ein objekt ist und welche methoden dieser implementiert.

    warum nicht so?:

    $di->get('\framework\iLogger');

    der unterschied ist, dass der DI Container nun die richtige Klasse für das Interface zurück gibt. Als Key kann also nur ein Interface verwendet werden.

    Es sollte ja sowieso so sein, das jedes Interface nicht einen gewissen concern angehaftet ist. demnach sollte dies doch völlig problemlos möglich sein?

    ich wäre auch super dankbar wenn ihr mir näher bringen könntet, warum meine lösung schlecht ist

  • #2
    Problematisch wird das, wenn Du mehrere Objekte der gleichen Klasse bzw. des gleichen Interfaces, aber mit verschiedenen Konfigurationen willst.

    Kommentar


    • #3
      ist es nicht so, dass wenn 2 gleiche objekte 2 unterschiedliche Konfigurationen hätten, dass diese dann unterschiedliche interfaces implementieren müssten, abhängig vom kontext? macht es sinn der klasse vorzuenthalten, dass dies unterschiedliche objekte sind?

      Kommentar


      • #4
        dass diese dann unterschiedliche interfaces implementieren müssten
        Vielleicht irre ich mich, aber ich finde, nicht. Bsp.: Man hat einen File-Handler, der vielleicht mit einem Verzeichnis initialisiert werden muss. Gleiches Interface, Gleiche Klasse, aber unterschiedliche Parameter. So, wie Du das beschreibst, müsste ja für jeden Service ein eigenes Interface vorhanden sein.

        macht es sinn der klasse vorzuenthalten, dass dies unterschiedliche objekte sind?
        Die Klasse weiß doch davon gar nichts.

        Kommentar


        • #5
          Nö warum? Stell Dir bspw. vor, du verwendest zwei Datenbanken. Damit auch zwei DatabaseConnections => 2 unterschiedliche Konfigurationen, dasselbe Interface.

          Interface injection ist im Grunde 'ne coole Sache, aber "unter der Haube" müssen Services immer einen eindeutigen Identifizierer haben. Alles weitere ist i.d.R. "syntaktic Sugar" und wird auf den eigentlichen DI draufgepappt. So kann man Services "guessen" (indem man bspw. Signaturen analysiert), eine andere herangehensweise ist unter anderem das Mapping durch NamingConventions von Properties (so macht Grails das in seinen Controllern) => "myMailerService" oder so. Das "Problem" heißt generell "ServiceLocator vs. Injection" und ist hier beschrieben: http://bit.ly/5yMot

          Kommentar


          • #6
            Zitat von joshiausdemall Beitrag anzeigen
            Stell Dir bspw. vor, du verwendest zwei Datenbanken. Damit auch zwei DatabaseConnections => 2 unterschiedliche Konfigurationen, dasselbe Interface.
            das wäre ja völlig unproblematisch, wenn die datenbanken jedoch unterschiedliche aufgaben haben, sind sie nach meinem Verständnis unterschiedliche services und sollten unterschiedliche interfaces implementieren. für jeden service den man injizieren wollen möchte, müsste man selbstverständlich mind. ein interface hinterlegen.

            aber ich verstehe schon die problematik, auch sollte ein interface nichts über die belange und den kontext wissen in dem es verwendet wird. bei meiner lösung wäre dies ja der fall.

            indirekt besteht die problematik ja aber auch bei dem klassischen di container. $di->get('db') gibt ja im rahmen der di instanz immer identisches objekt mit identischer konfiguration zurück. wenn die klasse plötzlich mit einer anderen datenbank agieren muss, wird die klasse ja einen anderen schlüssel nutzen $di->('db2') omit die grundproblematik weiterhin bestehen würde.

            ich werde mich nochmal intensiv damit auseinandersetzen und mal schauen welche lösungen noch gangbar wären, insbesondere stört mich die nicht vorhandene typensicherheit.

            Zitat von joshiausdemall Beitrag anzeigen
            und ist hier beschrieben: http://bit.ly/5yMot
            vielen dank, schaue ich mir an, vielleicht erleuchtet mich das ja

            Kommentar


            • #7
              omit die grundproblematik weiterhin bestehen würde.
              Welche Grundproblematik?

              Kommentar


              • #8
                Zitat von xm22 Beitrag anzeigen
                Welche Grundproblematik?
                die aufgeführte problematik war, dass wenn man identische klassen mit unterschiedlichen konfigurationen hat, welche beide identisches interface besitzten, meine idee nicht aufgehen kann. Beispiel waren 2 datenbanken mit mit unterschiedlichen konfigurationen.

                ein di container kann jedoch unter einem schlüssel auch nur ein service mit einer konfiguration anbieten. demnach müsste man für die 2. datenbank einen 2. service definieren der identische klassen mit einer anderen konfiguration anbietet.

                betroffene klassen müssen jedoch von dem service wissen, ansonsten können sie diesen nicht ansprechen.

                demnach handelt es sich in meinen augen nicht um den selben service, sonderen es gibt 2 verschiedene services welche unterschiedliche aufgaben erfüllen und demnach auch ein unterschiedliches interface gerechtfertigt wäre.


                mich stört, dass man services willkürlich tauschen kann und man keinerlei möglichkeit hat wirklich typensicher ohne viel gecaste zu arbeiten. wenn man jeden service gegen ein interface implementieren müsste wäre dieses problem quasi nicht mehr vorhanden.

                Kommentar


                • #9
                  mir fällt grade auf, dass das großer unfung war. durch eine contructor oder einer setter injection ist ja wieder typensicherheit gewährleistet.

                  PHP-Code:
                  /**
                  * @inject document
                  */
                  public function getDocumentService(/xyz/document $document); 

                  Kommentar


                  • #10
                    Ah, ich verstehe.. Stimmt, das sind zwei verschiedene Services. Aaaaaber, um beim Bsp. mit den Datenbanken zu bleiben. Angenommen, Dein Interface für 'db' sieht so aus:
                    PHP-Code:
                    interface DB {
                        public function 
                    setUsername();
                        public function 
                    setPassword();
                        
                    //...

                    Da würdest Du jetzt für 'db2' ein weiteres, bis auf den Namen identisches Interface bauen?

                    betroffene klassen müssen jedoch von dem service wissen, ansonsten können sie diesen nicht ansprechen.
                    Logisch - Aber ansonsten müssten sie auch von dem Interface wissen. Das ist gehüpft wie gesprungen.

                    wenn man jeden service gegen ein interface implementieren müsste wäre dieses problem quasi nicht mehr vorhanden.
                    Kann ich nachvollziehen, aber ich finde trotzdem, da macht man sich unheimlich viel Arbeit und schafft enorm viel Redundanz. Und Wenn Du Dich schon bei der Varianten mit den Service-Bezeichnern nicht darauf verlassen kannst, dass Du einen korrekten Service zurück bekommst, dann kannst Du das mit den Interfaces auch nicht.


                    EDIT: K.A., ob Du den Thread gelesen hast: http://www.php.de/software-design/81...container.html Das ist mein Ansatz, allerdings halt mit Bezeichnern statt Interfaces.


                    EDIT: Ich verstehe jetzt nicht ganz, wo Du Typsicherheit vermutet hast: Bei den Parametern, die der aufgerufene Service bekommt oder ging es um den Service, der zurück gegeben wurde?

                    Kommentar


                    • #11
                      mir fällt grade auf, dass das großer unfung war. durch eine contructor oder einer setter injection ist ja wieder typensicherheit gewährleistet.
                      Richtig.

                      , sind sie nach meinem Verständnis unterschiedliche services und sollten unterschiedliche interfaces implementieren. für jeden service den man injizieren wollen möchte, müsste man selbstverständlich mind. ein interface hinterlegen.
                      Ne, das ist ein falsches Verständnis. Alle Connections teilen das gleiche Interface, nur die Implementierung ist unterschiedlich. Sonst würd´s doch keinen Sinn machen, oder?

                      Außerdem: Der Sinn eines DIC ist gerade die Austauschbarkeit der Komponenten und die Entkopplung selbiger. Wenn Du als Adresse jeweils den vollqualifizierten Namespace-Name verwendest - was hättest Du dann gewonnen?

                      Kommentar


                      • #12
                        Zitat von joshiausdemall Beitrag anzeigen
                        Der Sinn eines DIC ist gerade die Austauschbarkeit der Komponenten und die Entkopplung selbiger. Wenn Du als Adresse jeweils den vollqualifizierten Namespace-Name verwendest - was hättest Du dann gewonnen?
                        Nur weil 2 Services identische interfaces implementieren sind es noch lange nicht identische services. Solang die Aufgabe der Services identisch ist, nutzen sie identisches Interface. das könnte z.b. bei Datenbanktreibern der fall sein.

                        wenn allerdings 2 datenbankverbindungen existieren, dann muss eine davon eine andere aufgabe erfüllen als die andere. im Klartext bedeutet das für mich wir haben unterschiedliche services. beiden services impementieren demnach ein eigenes interface.

                        Sinn eines DIC ist ja nicht, dass man willkürlich irgendwelche objekte injizieren kann sondern, dass man ein system austauschbar hält. um ein system so aufzubauen sind Verträge von nöten, alles was man ohne vertrag blind austauscht ist einer meinung nach grob fahrlässig da es keinerlei gewährleistung dafür gibt, dass die klassen miteinander arbeiten können.

                        Kommentar


                        • #13
                          Dass es sich um verschiedene Services handelt, gebe ich Dir recht, aber
                          dann muss eine davon eine andere aufgabe erfüllen als die andere
                          Was wäre denn, wenn per Zufallsprinzip eine von beiden ausgewählt wird (quasi Load-Balancing). Dann trotzdem noch zwei Interfaces?

                          alles was man ohne vertrag blind austauscht ist einer meinung nach grob fahrlässig da es keinerlei gewährleistung dafür gibt
                          Wie schon gesagt: Die Übergabe eines Interface-Namens ist doch nichts anderes als die Bezeichnung eines Services, den Du unter dieser Adressierung findest. Genau so wie bei dem anderen Ansatz. Du kannst Dich also genauso wenig auf den Typ der Rückgabe verlassen.

                          Kommentar


                          • #14
                            Zitat von xm22 Beitrag anzeigen
                            EDIT: Ich verstehe jetzt nicht ganz, wo Du Typsicherheit vermutet hast: Bei den Parametern, die der aufgerufene Service bekommt oder ging es um den Service, der zurück gegeben wurde?
                            wenn du via namen auf einen service zugreifst musst du

                            1. seinen namen wissen
                            2. seinen typ wissen

                            $di->get('logger')->log('LOG!');

                            ist nicht praktikabel weil du nicht sicher gehen kannst das der serive logger auch die funktion log implementiert. wenn du generell sagst, jeder service muss ein interface sein ist dies völlig legal:

                            $di->get('ilogger')->log('LOG!');

                            du bekommst einfach die aktuelle klasse welche auf das interface ilogger gemappt ist und kannst sichergehen, dass die log funktion existiert.

                            Kommentar


                            • #15
                              ist nicht praktikabel weil du nicht sicher gehen kannst das der serive logger auch die funktion log implementiert.
                              Schon klar, aber Du kannst genauso wenig davon ausgehen, dass das Interface ilogger auch die Methode log implementiert.. An irgendeiner Stelle musst Du Dich darauf verlassen, dass Du ein passendes Objekt bekommst.

                              Kommentar

                              Lädt...
                              X