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 :

Typage des paramètres d'une classe abstraite [PHP 5.4]


Sujet :

Langage PHP

  1. #1
    Membre confirmé
    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
    Points : 470
    Points
    470
    Par défaut Typage des paramètres d'une classe abstraite
    Bonjour,

    Je voudrais réaliser un objet abstrait mapper dont héritera tous les objets destinés a la gestion de la persistance des données. J'ai à coté de ça une classe abstraite domainObject dont hérite tous les objets métier.

    Si on prend par exemple une table client, relative à l'objet client, on a donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    class MapperClient extends mapper {...}
    class client extends domainObject {...}
    ou pour les fournisseurs
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    class MapperFournisseur extends mapper {...}
    class fournisseur extends domainObject {...}
    A l'usage, dans le code, l'instanciation d'un client d'ID $id_du_client se fait comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $mc = new MapperClient();
    $obj = new client();
    $mc->Load($client, $id_du_client);
    c'est relou, mais je code SOLID en séparant bien les rôles des class et j'évite les méthodes statics.

    Voilà pour le préambule. Maintenant, la méthode Load est définie comme méthode abstraite dans mapper. Actuellement, je la définie comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    abstract public function Load(domainObject $obj, $id);
    or, il est bien évident qu'au moment de la définition de cette méthode dans chacune des class dérivées, $obj ne sera plus de type domainObjet mais d'un type qui en dérive. Concrètement, pour MapperClient, j'écrirais alors :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class MapperClient extends mapper
    {
          public function Load(client $client, $id) {...}
    }

    Pour moi, cela ressemble à une surcharge plutot qu'a une re-définition... Donc est-ce que cela est sémantiquement et philosophiquement correct en POO, ou faudrait-il continuer à définir $client comme un domainObject ? ou autre chose ?

    En vous remerciant.

  2. #2
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Salut,

    En théorie POO : une méthode redéfinie doit posséder exactement la même signature que la méthode parente.
    En effet, dans ton cas de figure c'est plus de la surcharge que de la redéfinition.

    Par ailleurs, il est tout à fait possible de préciser le type d'un paramètre dans une fonction appartenant à une classe dérivée.
    Tu dois juste faire attention au type de manière que ce genre de code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    parent::parentFunction($param);
    ne fasse pas planter l'exécution à cause d'un mauvais typage de $param lors de l'appel de la fonction parente.
    Si cette règle est respectée, pas de souci.

    Toutes tes classes dérivent de domainObject donc sont, de fait, des instances de domainObject et comme la fonction racine attend un paramètre de type domainObject cela n'est pas problématique et respecte les principes de la POO.
    Après c'est selon, si tu souhaites te taper la réécriture de cette fonction pour chaque type...

    Il est même possible de surcharger complètement une fonction avec les types que tu veux si tu ne fais pas appel à la fonction parente...

  3. #3
    Membre confirmé
    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
    Points : 470
    Points
    470
    Par défaut
    Merci pour cette réponse !

    Le problème que ça me pose, c'est que mapper::Load(domainObject $obj, $id) étant abstraite, n'importe quel objet dérivant de mapper peut accepter en paramètre pour Load() n'importe quel objet dérivant de domainObject, alors que je ne veux pas.

    En fait, il y a 3 choses que j'aimerai que mon code retranscrive :

    - la méthode Load doit forcément être définie dans les classes héritées de mapper (d'où le abstract)
    - la méthode Load doit toujours être définie avec un objet héritant de domainObject en paramètre
    - la méthode Load ne devrait pouvoir prendre qu'un et un seul type d'objet hérité de domainObject

    il ne faut donc pas que clientMapper puisse accepter en paramètre un objet de type fournisseur qui hériterait pourtant lui aussi de domainObject.

    Et là, j'avoue que je ne vois pas comment architecturer le code pour remplir ces conditions...

    Je précise que je tiens à garder le typage le plus fort possible, mais je suis sur que ça ne t'auras pas échappé ^^

    Encore merci

  4. #4
    Membre émérite

    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
    Points : 2 440
    Points
    2 440
    Par défaut
    Est-ce que Abstract est préférable à une interface dans ton cas?

    De toutes façons, si tu veux rester SOLID, tu ne peux pas faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public function Load(client $client, $id)
    Il te faut passer une abstraction/interface et pas un objet concret.

    Idées:
    - soit tu crées une interface spécifique pour chaque mapper (et tu utilises un trait pour les méthodes partagées)
    - soit ta méthode load() ne fait que déléguer le traitement à une méthode privée qui prend une instance plus typée en argument. Dans ce cas, tu ne surcharges plus load().

  5. #5
    Membre confirmé
    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
    Points : 470
    Points
    470
    Par défaut
    Je dévie un poil du sujet de base, mais je débute un peu en SOLID, et c'est pas vraiment naturel pour quelqu'un qui à toujours fait de l'activeRecord. Il me semblait qu'a partir du moment ou mon mapper n'avait pour seul rôle que de faire l'interface entre la BDD et l'objet métier, l'implémentation importait peu... Aussi puis-je te demander en quoi ce n'est pas SOLID de passer un objet concret ?

    Et du coup, est-ce que

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class mapperClient
    {
      public function Load($id)
      {
         $client = new client();
         ...
         return $client;
      }
    }
    est SOLID ?

    Merci

  6. #6
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Citation Envoyé par comode Voir le message
    Aussi puis-je te demander en quoi ce n'est pas SOLID de passer un objet concret ?
    Parce que cela enfreint le O de SOLID

    • Single responsibility principle
    • Open close principle
    • Liskov principle
    • Interface segregation principle
    • Dependency inversion principle

    Le Open close principle précise que Classes, methods should be open for extension, but closed for modifications.
    Dans ton cas de figure, si tu types le paramètre avec une classe réelle, tu vas enfreindre l'aspect ouverture du code.

  7. #7
    Membre émérite

    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
    Points : 2 440
    Points
    2 440
    Par défaut
    Pour continuer ce que dit Rawsrc,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Load(Client $client, $id)
    viole SOLID en passant une classe concrète, et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public function Load($id)
      {
         $client = new client();
         ...
         return $client;
      }
    viole SOLID (le D plus précisément) aussi, en instanciant un client dans la méthode.

  8. #8
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Pour continuer ce que dit Tsilefy,

    Le D de SOLID stipule que les classes de haut niveau ne doivent pas travailler directement avec celles de bas niveau. Concrètement : il faut casser la dépendance en passant par une couche d'abstraction.
    Le respect de ce principe induit que les méthodes de haut niveau ne devraient pas faire appel au mot clé new pour instancier des classes.
    Le moyen pour y parvenir c'est de faire appel aux designs patterns : Factory Method, Abstract Factory, Prototype, Template...

    Comme toujours ce n'est qu'une recommandation, il faut savoir raison garder sinon je te dis pas la complexité du code... Il faut l'appliquer aux parties du code non statiques.

  9. #9
    Membre confirmé
    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
    Points : 470
    Points
    470
    Par défaut
    Merci beaucoup pour ces éclaircissements !

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

Discussions similaires

  1. Réponses: 17
    Dernier message: 14/03/2012, 17h55
  2. [PHP 5.3] Typage des paramètres d'une fonction pour ou contre
    Par berceker united dans le forum Langage
    Réponses: 6
    Dernier message: 09/07/2009, 15h34
  3. Réponses: 10
    Dernier message: 13/09/2006, 09h13
  4. [Débutant]Passer une classe abstraite en paramètre
    Par Invité dans le forum Débuter
    Réponses: 2
    Dernier message: 06/01/2006, 17h56
  5. Réponses: 2
    Dernier message: 27/03/2005, 16h09

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