IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Injection de dépendances et LazyLoading, comment faire ?


Sujet :

Langage PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre chevronné
    Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    504
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 504
    Par défaut Injection de dépendances et LazyLoading, comment faire ?
    Bonjour,

    Voilà, je trouve des tartines et tartines d'explications et exemples sur les bienfaits de l'injection de dépendances, mais absolument rien sur l'opération inverse...

    Je m'explique. Admettons que nous ayons une classe CUtilisateur que l'on stockerait dans une BDD MySQL dans une table appelé utilisateur (au hasard).

    On aurait alors un lien assez direct entre la classe CUtilisateur et la table utilisateur, mais en tant que gens bien élevé, on construirait une classe intermédiaire (disons RUtilisateur) pour renseigner la class CUtilisateur à partir d'utilisateur et inversement.

    Ces utilisateurs auraient parmis leur propriété un pays d'origine, faisant lui même l'objet d'une class CPays, d'une table pays (lié par une clef étrangère à utilisateur) et d'un RPays pour assurer le lien entre la table et class du même nom.

    Edit : RPays est donc un genre de Mapper... Moi, j'ai appelé ça un répository, d'où le R

    Sur ce principe, pour faire de l'injection de dépendance, l'action de spécifier un pays pour un utilisateur devrait ressembler à quelque chose un peu comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $bdd = PDOConnect(...); // ma connexion à la BDD 
    $r_pays = new RPays($bdd);
    $pays = $r_pays->load('Belgique'); // pays est une instance de CPays retournée et remplies par RPays
     
    $r_u = new RUtilisateur($bdd);
    $u = $r_u->Load(3758); // on charge l'utilisateur 3758 de la BDD dans l'instance de class CUser retournée par RUser.
     
    $u->setPays($pays); // affectation d'un nouveau pays
    $r_u->Save($u, $bdd); // sauvegarde de l'information
    Autant que je sache, j'ai donc respecté les principes d'injection de dépendance et n'ai pas fortement liée mes class entre elles.

    Dans la pratique, la class CUtilisateur devrait s'écrire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class CUtilisateur {
       private $_pays = NULL;
       public function setPays(IPays $pays) { // tant qu'a faire, on réclame une interface plutôt qu'une class, donc IPays qu'on supposera déclaré
          $this->_pays = $pays;
       }
    }
    Arrêtez moi si je fais erreur quelque part.

    C'est maintenant que ça se complique

    En effet, Pays est lui même relié à une class CLangue elle même reliée à une class CAlphabet etc... toutes ces class existante elles aussi sous forme de tables dans la base de données.

    Il est bien entendu inconcevable qu'au moment où je charge un utilisateur, je charge également toutes les class associées, donc je souhaite opter pour une solution de LazyLoading, à savoir que pour chaque class que j'instancie et charge à partir de la BDD, je ne charge/instancie les class liées qu'au moment où j'en ai besoin.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class CUtilisateur {
     
       private $_pays = NULL;
       private $_id_pays = 0;
     
       public function setPays(IPays $pays) { // tant qu'a faire, on réclame une interface plutôt qu'une class, donc IPays qu'on supposera déclaré
          $this->_pays = $pays;
          $this->_id_pays = $pays->GetId();
       }
     
       public function GetPays() { 
          if(!is_object($this->_pays))
          {
             // tout ce que je sais ici, c'est que je suis associé au pays dont l'ID est contenu dans $this->_id_pays.
             // Cette données à été renseignée par RUtilisateur->load();
             // QUESTION : COMMENT JE FAIS ICI ????
          }
          return $this->_pays;
       }
     
    }
    La question est donc comment je fait pour que mon getter qui ne connait que l'ID du pays associé me retourne une instance de CPays sans briser mes efforts pour de pas faire interdépendre mes class ?

    J'avais d'abord pensé injecter une instance de RPays à CUtilisateur::GetPays(), mais il me semble que ça serait une erreur dans la mesure où tout est fait en amont pour que CUtilisateur ignore l’existence des class destinées à assurer la persistance des données.

    Votre avis ?

    En vous remerciant.

  2. #2
    Membre Expert

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Par défaut
    Je ne comprends pas ta question.

    Si $this->_pays n'existe pas, c'est parce qu'elle n'a pas été renseignée. Dans ce cas, tu lances une CountryNotFoundException. Ça te permet de re-hydrater Utilisateur avec un pays tout neuf quand tu en as besoin, puisque tu ne veux pas tout charger au départ.

  3. #3
    Membre chevronné
    Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    504
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 504
    Par défaut
    non mais je ne demande pas pourquoi le code ne marche pas (c'est même pas un vrai code, j'ai écrit ça a la volée), c'est une question de design pattern en fait. En l'occurrence, comment instancier le CPays qui est dans CUtilisateur lors de l'appel de CUtilisateur::GetPays() en respectant le principe d'injection de dépendance et sans créer d'interdépendance forte entre les class.

    Une exeption pourrait en effet être une piste... mais je ne crois pas que ça me simplifiera les choses... je cherche quelque chose de plus... conventionnel...

  4. #4
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Lead dév - Architecte
    Inscrit en
    Août 2003
    Messages
    6 693
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Lead dév - Architecte
    Secteur : Industrie

    Informations forums :
    Inscription : Août 2003
    Messages : 6 693
    Par défaut
    Je réponds pas à ta question , mais clairement tu sur-concois un peu le problème.
    Avoir une cascade d'objets quand un ou deux objets pourraient sans doute suffire n'est pas forcément l'idéal.

    A vouloir absolument tout modéliser sous forme d'objet tu vas tomber dans un code infernal.
    Pour illustrer un peu , au lieux de faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $user->getLang(); //fr_Fr
    tu vas devoir faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $user->GetPays()->GetLang()->toString();//fr_Fr
    Ne pas oublier le principe KISS

    Et pour conclure , un peu de lecture : http://blog.codinghorror.com/your-code-oop-or-poo/ :
    Stop worrying so much about the objects. Concentrate on satisfying the principles of object orientation rather than object-izing everything
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #5
    Membre chevronné
    Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    504
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 504
    Par défaut
    Comme je disais, c'est pas un vrai code, c'est une illustration pour parler d'un probleme de design pattern.

    Dans la réalité, c'est un problème que je rencontrerai forcément...

  6. #6
    Membre Expert

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Par défaut
    Citation Envoyé par comode Voir le message
    En l'occurrence, comment instancier le CPays qui est dans CUtilisateur lors de l'appel de CUtilisateur::GetPays() en respectant le principe d'injection de dépendance et sans créer d'interdépendance forte entre les class.
    .
    Tu ne peux pas, c'est contre le principe même de l'inversion de dépendance. À partir du moment où tu instancies CPays dans CUtilisateur (que ce soit en utilisant new Cpays ou en utilisant d'autres techniques) au lieu de l'injecter, tu ne fais plus de l'injection de dépendances.

  7. #7
    Membre chevronné
    Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    504
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 504
    Par défaut
    C'est hélas la seule conclusion à laquelle j'aboutis moi aussi...

    Comment font les trucs du genre de symphony2 pour gérer ce genre de cas de figure en arrière plan ? Il n'instancie quand même pas toutes les class référencées dans les propriétés d'un objet à chaque fois qu'on créé une nouvelle instance ?

  8. #8
    Membre Expert

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Par défaut
    Soit ils instancient la classe, soit ils réutilisent un objet déjà disponible. C'est pour cette raison qu'ils utilisent un Conteneur à injection de dépendances, qui s'occupe d'initialiser les objets nécessaires, dans le bon ordre, avec les bons paramètres.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 17/05/2010, 22h18
  2. Réponses: 0
    Dernier message: 08/05/2010, 17h57
  3. Création de logiciel, oui, mais quelle base de donnée ?
    Par Acti dans le forum Décisions SGBD
    Réponses: 3
    Dernier message: 17/02/2005, 12h41
  4. HomeDB, oui mais comment ?
    Par Gregouz dans le forum Décisions SGBD
    Réponses: 1
    Dernier message: 27/10/2004, 15h27
  5. [APPLET-SERVLET] download oui mais upload non ...
    Par meufeu dans le forum Applets
    Réponses: 7
    Dernier message: 09/08/2004, 14h36

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo