2
votes

Nombre maximum de classes enfants acceptables

En utilisant PHP Mess Detector https://phpmd.org , j'ai trouvé une règle qui me dit de refactoriser une classe avec plus de 15 enfants.

Ma classe dans cette question est une classe de base pour les référentiels qui implémente diverses méthodes que chaque référentiel doit avoir.

Ma question est la suivante.

J'ai toujours pensé que le plus grand avantage de l'héritage dans la programmation orientée objet était d'éviter la répétition du code, alors comment pourrait-il être un problème si une classe était utilisée par de nombreux enfants?

La règle est la suivante:

<rule name="NumberOfChildren"
      since="0.2"
      message = "The {0} {1} has {2} children. Consider to rebalance this class hierarchy to keep number of children under {3}."
      class="PHPMD\Rule\Design\NumberOfChildren"
      externalInfoUrl="https://phpmd.org/rules/design.html#numberofchildren">
    <description>
    </description>
    <priority>2</priority>
    <properties>
        <property name="minimum" value="15" description="Maximum number of acceptable child classes." />
    </properties>
    <example />
</rule>

J'apprécie toute aide pour clarifier ce doute.


4 commentaires

Je n'utilise jamais extend , je n'utilise pas de traits ni Dieu interdit les classes abstraites. J'ai des tonnes d'interfaces et d'implémentations, chacune claire et ciblée, facile à entretenir et à tester. PHPMD n'a rien à redire lors de l'inspection de ma base de code. C'est donc faisable… c'est votre conception et vos décisions qui sont mauvaises.


Mais n'est-il pas judicieux d'avoir une classe de référentiel abstraite, vous pouvez donc par exemple implémenter une méthode findOrFail générique en utilisant la méthode fail par exemple? ou implémentez une méthode de suppression générique.


C'est un raccourci, ce n'est pas intelligent, je n'utilise pas de raccourcis. Les méthodes de suppression / recherche sont disponibles dans les référentiels Doctrine que j'injecte dans mes référentiels.


Exemple de référentiel: gist.github.com/mikemix/c0092208216e4f07dd0f0f0be749 J'ai des dizaines de ces référentiels.


3 Réponses :


-2
votes

Utilisez le trait au lieu de la classe. Le trait n'a pas de limite comme:

  class child {
     use ParentTrait;
  }

Donc, vous pouvez utiliser ceci dans toutes les classes comme

trait ParentTrait {...}


6 commentaires

Merci de votre aide. L'utilisation d'un trait résoudra le problème. Mais pourriez-vous expliquer la raison de la restriction des classes pour enfants?


Les traits sont généralement utilisés pour les petites tâches et les modifications - en ajoutant un comportement, ils sont un bon outil mais je ne les utiliserais pas pour des hiérarchies de classes aussi importantes


Je pense que le trait ne devrait pas être utilisé dans ce contexte. Les traits sont lorsque vous avez quelque chose de général que vous voulez dans plusieurs classes. Un bon exemple si vous voulez que les modèles envoient des e-mails. Créez un trait SendEmail et utilisez-le là où il doit être utilisé.


Alors, quelle est votre suggestion concernant son problème?


J'ai répondu à la question, mais cela ne semble pas acceptable de dire que l'extension d'un référentiel est une décision de conception équitable. Je ne pense pas qu'il ait tort sur celui-ci.


étendre les référentiels, je ne ferais pas cela, quand vous faites cela, vous n'avez probablement pas un bon modèle de domaine et une bonne hiérarchie de classes étendre les objets du référentiel à 15 niveaux de profondeur sent assez avec un gros désordre, mais sans code, c'est difficile à dire



-3
votes

Des problèmes peuvent également survenir avec l'héritage beaucoup d'héritage provoque beaucoup de dépendances sur le code sous-jacent, imaginez que vous avez deux classes qui sont des sous-classes de la classe parente, vous ajoutez des fonctionnalités dans la classe parente mais vous faites une erreur et vous freinez tout de vos classes et les chances que cela se produit augmente avec la hiérarchie croissante. Habituellement, les frameworks le résolvent par composition / agrégation et injection de dépendances.


0 commentaires

0
votes

D'après mon expérience de travail avec les outils de renifleur, c'est plus une odeur qu'une erreur. J'ai l'impression que le choix de conception que vous avez fait est totalement la bonne manière et qu'un trait ou toute autre solution est plus un hack.

Alors, prenez tout ce qu'il vous dit en considération, analysez-le et si vous pensez toujours que c'est le bon chemin, gardez-le.

L'odeur peut être si vous faites quelque chose de général est étendu, il peut être utilisé dans de nombreux domaines différents. Donc, cela peut être quelque chose dont vous devriez concevoir votre chemin.


8 commentaires

Je ne suis pas d'accord avec cela, bien sûr que cela dépend toujours de votre logiciel


Dans de nombreux cas, cela pourrait être un problème, mais je pense que c'est la même chose que de dire que le modèle est étendu à beaucoup ou que la classe BaseController est étendue à fortement. Non ils ne sont pas. Ils sont nécessaires pour que le cadre fonctionne. Mon expérience dit également que vous finissez souvent par créer des référentiels plus spécifiques dans le temps.


@anjalis que dans le cas de BaseController devrions-nous également convertir cela en trait, alors nous perdons le polymorphisme des classes?


je ne dis pas qu'il devrait être converti en trait, si vous cochez ma réponse, cela explique pourquoi je ne suis pas d'accord avec votre réponse


Mais alors vous perdez le polymorphisme? Que se passe-t-il s'il a besoin de passer un repostiry inconnu à une classe, c'est une bonne conception d'avoir une classe abstraite à définir comme paramètre.


Je ne dis pas que vous ne devriez pas du tout utiliser le polymorphisme, utilisez-le simplement avec sagesse et une hiérarchie aussi profonde semble ne pas avoir été utilisée à bon escient regardez en.wikipedia.org/wiki/Composition_over_inheritance


Je suis d'accord, mais pas dans le cas d'un référentiel. C'est une façon tellement générique de le faire, Symfony a même un baseRepository dont vous pouvez hériter de github.com/doctrine/orm/blob/master/lib/Doctrine/ORM/... donc vous chargeriez cela avec la composition? cela va être un désordre dans la base de code, il faut implémenter beaucoup de méthodes de proxy génériques de plaque chauffante juste pour appeler le référentiel composé.


Même avec les considérations contre cette réponse, je crois que c'est toujours la plus appropriée pour répondre à mon doute. Merci à tous pour l'aide.