67 votes

Comment puis-je utiliser l'injection de dépendances dans de simples fonctions php, et dois-je m'en préoccuper ?

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

Je me demande s'il s'agit d'une solution au problème du "je passe tout le temps les connexions aux bases de données comme arguments".

J'ai essayé de lire l'article de wikipedia à ce sujet, mais l'exemple est écrit en Java et je ne comprends pas bien la différence qu'il essaie de faire comprendre. ( http://en.wikipedia.org/wiki/Dependency_injection ).

J'ai lu cet article de dependency-injection-in-php ( http://www.potstuck.com/2009/01/08/php-dependency-injection/ ), et il semble que l'objectif ne soit pas de passer les dépendances à un objet directement, mais de boucler la création d'un objet avec la création de ses dépendances. Je ne suis pas sûr de savoir comment appliquer cela dans un contexte d'utilisation des fonctions php, cependant.

De plus, est-ce que ce qui suit est de l'injection de dépendances, et est-ce que je devrais me donner la peine d'essayer de faire de l'injection de dépendances dans un contexte fonctionnel ?

Version 1 : (le genre 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 : (il n'est pas nécessaire 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 séparée, 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 juste un aperçu qui rend la méthode et les avantages -cristallins- clairs ?

75voto

Arkh Points 5804

L'injection de dépendances est un grand mot pour dire "J'ai quelques paramètres supplémentaires dans mon constructeur".

C'est ce que vous faisiez avant l'affreuse vague Singleton lorsque vous n'aimiez pas les globaux :

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

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

Maintenant, l'astuce est d'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 avez juste une autre classe et plus de complexité. Mais, votre classe utilisateur peut avoir besoin de quelque chose pour enregistrer des messages comme beaucoup d'autres classes. Il vous suffit d'ajouter une fonction getMessageHandler à votre conteneur de dépendances, et des fonctions $this->_messages = $di->getMessageHandler() à votre classe d'utilisateur. Rien à changer dans le reste de votre code.

Vous obtiendrez beaucoup d'informations sur doc de symfony

13voto

jmoz Points 984

Votre premier exemple IS l'injection de dépendance, vous injectez la dépendance de l'objet de la base de données dans la fonction.

Sarah a dit que ce n'était pas le cas, mais je pense que ça l'est. Je crois qu'elle pense aux conteneurs d'injection de dépendances qui sont le niveau supérieur :

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

7voto

Sarah Happy Points 233

Aucun de vos exemples ne ressemble à une injection de dépendance, la version 1 est la plus proche cependant. 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 passés par le créateur de l'instance (qui pourrait être une usine, un test, ou un cadre d'injection de dépendances).

Pour contourner le problème du "passage systématique de l'objet de connexion", vous pouvez envisager le modèle de modèle. Le modèle de template est essentiellement 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. En fait, la base est un modèle de bloc de code, et les méthodes abstraites sont les blancs à remplir. J'utilise personnellement le modèle de méthode template pour réaliser mon contrôle de ressources de base de données en Java.

2voto

Ben Points 414

J'ai moi-même effectué de nombreuses recherches sur ce sujet (PHP Dependency Injection) et je n'ai pas trouvé grand chose à mon goût. Beaucoup de choses ont été écrites sur le sujet pour d'autres langages (Google Guice - http://code.google.com/p/google-guice/ (Java Spring), mais je n'ai pas trouvé grand chose de disponible pour PHP. Cependant, quel que soit le langage, les défis sont similaires.

Les trois versions que vous énumérez dans votre question sont l'approche typique. La version 3 est la plus proche de la direction que j'ai vue prendre par l'industrie. En transférant la responsabilité de la création de vos objets dépendants en dehors de votre classe, vous êtes libre de les manipuler comme bon vous semble 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 peuvent potentiellement ne même pas être utilisés par l'objet récepteur, mais qui sont transmis à un objet dépendant secondaire. Cela devient désordonné et vous perdez la connaissance de ce qui vient d'où.

L'exemple du conteneur de dépendances de @Arkh et @mmmshuddup est un excellent début, mais j'ai néanmoins trouvé des limites à cette approche également. La solution finale à laquelle j'ai abouti est une solution personnalisée, modelée un peu sur le Cake Pattern populaire en Scala. Elle vous permet de passer une seule dépendance dans chacun de vos constructeurs ET elle vous permet de définir la construction par défaut des objets dépendants par classe. Cela vous évite de longues chaînes de dépendances et 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 ai été très satisfait. 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 ainsi que les détails de votre question. http://developers.blog.box.com/2012/02/15/introducting-diesel-php-dependency-injection/

2voto

Jerome WAGNER Points 6622

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

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

Si vous codez en dur l'existence de B dans A, vous serez bloqué lorsque vous voudrez que A utilise les mêmes services, mais mis en œuvre par un autre composant.

En général, vous définissez une interface de service que B et C mettront en œuvre, et vous vous assurez que lorsque vous utilisez A, vous l'alimentez en objets compatibles avec l'interface requise.

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X