web-dev-qa-db-fra.com

Comment puis-je utiliser "Dependency Injection" dans des fonctions php simples, et devrais-je m'embêter?

J'entends des gens parler de l'injection de dépendance et de ses avantages tout le temps, mais je ne la comprends pas vraiment.

Je me demande si c'est une solution au problème "Je passe des connexions à la base de données comme arguments tout le temps".

J'ai essayé de lire l'entrée de wikipedia dessus, mais l'exemple est écrit en Java donc je ne comprends pas bien la différence qu'il essaie de clarifier. ( http: // en .wikipedia.org/wiki/Dependency_injection ).

J'ai lu cet article sur l'injection de dépendance en php ( http://www.potstuck.com/2009/01/08/php-dependency-injection/ ), et il semble que l'objectif soit pour ne pas transmettre directement les dépendances à un objet, mais pour boucler la création d'un objet avec la création de ses dépendances. Cependant, je ne sais pas comment appliquer cela dans un contexte d'utilisation des fonctions php.

De plus, l'injection de dépendance est-elle la suivante et dois-je prendre la peine d'essayer de faire l'injection de dépendance dans un contexte fonctionnel?

Version 1: (le type de code que je crée, mais que je n'aime pas, tous les jours)

function get_data_from_database($database_connection){
    $data = $database_connection->query('blah');
    return $data;
}

Version 2: (pas besoin de passer une connexion à la base de données, mais peut-être pas l'injection de dépendance?)

function get_database_connection(){
    static $db_connection;
    if($db_connection){
        return $db_connection;
    } else {
        // create db_connection
      ...
    }
}

function get_data_from_database(){
   $conn = get_database_connection();
   $data = $conn->query('blah');
   return $data;
}

$data = get_data_from_database();

Version 3: (la création de "l'objet"/des données est distincte, et le code de la base de données est toujours, donc peut-être cela compterait-il comme injection de dépendance?)

function factory_of_data_set(){
    static $db_connection;
    $data_set = null;
    $db_connection = get_database_connection();
    $data_set = $db_connection->query('blah');
    return $data_set;
}

$data = factory_of_data_set();

Quelqu'un a-t-il une bonne ressource ou simplement un aperçu qui rend la méthode et les avantages -cristallins- clairs?

67
Kzqai

L'injection de dépendances est un gros mot pour "J'ai encore plus de paramètres dans mon constructeur".

C'est ce que vous faisiez avant la terrible vague Singleton quand vous n'aimiez pas les mondiaux:

<?php
class User {
    private $_db;
    function __construct($db) {
        $this->_db = $db;
    }
}

$db   = new Db();
$user = new User($db);

Maintenant, l'astuce consiste à utiliser une seule classe pour gérer vos dépendances, quelque chose comme ça:

class DependencyContainer 
{
    private _instances = array();
    private _params = array();

    public function __construct($params)
    {
        $this->_params = $params;
    }

    public function getDb()
    {
        if (empty($this->_instances['db']) 
            || !is_a($this->_instances['db'], 'PDO')
        ) {
            $this->_instances['db'] = new PDO(
                $this->_params['dsn'],
                $this->_params['dbUser'], 
                $this->_params['dbPwd']
            );
        }
        return $this->_instances['db'];
    }
}

class User
{
    private $_db;
    public function __construct(DependencyContainer $di)
    {
         $this->_db = $di->getDb();
    }
}

$dependencies = new DependencyContainer($someParams);
$user = new User($dependencies);

Vous devez penser que vous êtes juste une autre classe et plus de complexité. Mais, votre classe d'utilisateurs peut avoir besoin de quelque chose pour enregistrer des messages comme beaucoup d'autres classes. Ajoutez simplement une fonction getMessageHandler à votre conteneur de dépendances et quelques $this->_messages = $di->getMessageHandler() à votre classe d'utilisateurs. Rien à changer dans le reste de votre code.

Vous obtiendrez beaucoup d'informations sur doc de symfony

75
Arkh

Votre premier exemple [~ # ~] est [~ # ~] injection de dépendance, vous injectez la dépendance à l'objet de base de données dans la fonction.

Sarah a dit que non, mais c'est le cas, je pense qu'elle pense aux conteneurs d'injection de dépendance qui sont au niveau supérieur:

http://components.symfony-project.org/dependency-injection/trunk/book/02-Dependency-Injection-Containers

13
jmoz

Aucun de vos exemples ne ressemble à une injection de dépendances, la version 1 est cependant la plus proche. L'injection de dépendances est une technique utilisée dans la programmation orientée objet, où le constructeur d'un objet a des arguments pour les objets de service dont il a besoin, et ces objets de service sont transmis par le créateur de l'instance (qui pourrait être une fabrique, un test ou un framework d'injection de dépendances).

Pour contourner votre problème de "toujours passer l'objet de connexion", vous pouvez envisager le modèle de modèle. Le modèle de modèle est fondamentalement une classe de base abstraite avec la partie commune d'un bloc de code répété et des méthodes abstraites pour permettre la variation entre les instances de ces blocs de code répétés. Fondamentalement, la base est un modèle d'un bloc de code, et les méthodes abstraites sont les blancs à remplir. J'utilise personnellement le modèle de méthode de modèle pour effectuer mon contrôle des ressources de base de données en Java.

7
Sarah Happy

J'ai moi-même fait beaucoup de recherches sur ce sujet (PHP Dependency Injection) et je n'ai pas trouvé grand-chose à mon goût. Beaucoup a été écrit sur le sujet pour d'autres langues (Google Guice - http://code.google.com/p/google-guice/ ; Java Spring ), mais je n'ai pas trouvé grand-chose de disponible pour PHP. Quel que soit le langage, cependant, les défis sont similaires.

Les trois versions que vous indiquez dans votre question sont l'approche typique. La version 3 est la plus proche de la direction dans laquelle j'ai vu l'industrie se diriger. En transférant la responsabilité de créer vos objets dépendants en dehors de votre classe, vous êtes libre de les manipuler à votre guise dans votre code de test. Cependant, le problème que j'ai rencontré avec cette approche est que vous vous retrouvez avec de longues chaînes d'objets dépendants dans votre constructeur qui ne peuvent même pas être utilisées par l'objet récepteur, mais sont transmises à un objet dépendant secondaire. Cela devient désordonné et vous perdez la connaissance de ce qui vient d'où.

L'exemple de conteneur de dépendance de @Arkh et @mmmshuddup est un bon début, mais j'ai néanmoins trouvé des limites à cette approche également. La solution finale sur laquelle je suis arrivé était une solution sur mesure modelée quelque peu sur le modèle de gâteau populaire à Scala. Il vous permet de passer une seule dépendance dans chacun de vos constructeurs ET il vous permet de définir la construction par défaut des objets dépendants par classe . Cela vous libère des longues chaînes de dépendances ainsi que de perdre le contrôle des implémentations par défaut de vos dépendances.

J'ai appelé le système Diesel et j'en suis très content. J'ai publié le code sur github pour toute personne intéressée. Vous pouvez y accéder à partir du blog que j'ai écrit sur le sujet, qui décrit l'utilisation de base et donne plus de détails sur votre question. http://developers.blog.box.com/2012/02/15/introducting-diesel-php-dependency-injection/

2
Ben

L'injection de dépendance est l'idée de supprimer la dépendance entre 2 composants afin de se concentrer sur la raison pour laquelle ils sont dépendants.

Imaginez que vous avez un composant A qui a besoin d'utiliser les services d'un autre composant B.

Si vous codez en dur l'existence de B à l'intérieur de A, vous serez bloqué lorsque vous voudrez que A utilise les mêmes services, mais implémenté par un autre composant.

Donc, généralement, vous définissez une interface de service que B et C implémenteront, et vous vous assurez que lorsque vous utilisez A, vous l'alimentez avec des objets compatibles avec l'interface requise.

Dans votre cas, vous pourriez considérer que votre interface est un service sur lequel vous pouvez effectuer une requête.

Votre premier cas est celui qui se rapproche le plus de l'idée de l'injection de dépendance.

2
Jerome WAGNER