Plusieurs classes avec le même nom, mais des espaces de noms différents?

11

J'ai rencontré du code (C # si c'est important) qui a des classes qui portent le même nom, mais qui diffèrent dans leurs espaces de noms. Ils ont tous tendance à représenter la même chose logique, mais sont souvent des «vues» différentes du même objet. Les différents espaces de noms font parfois partie de la même solution, et même de la même DLL.

J'ai mes pensées, mais je n'ai rien trouvé de définitif sur cette pratique à considérer comme une utilisation intelligente des outils disponibles ou un modèle à éviter.

Cette question sur le nommage des schtroumpfs touche à cela, mais dans l'autre sens. Une ambiguïté des noms nécessiterait probablement un préfixe arbitraire et omniprésent, que cette question voulait éliminer.

Quelles sont les lignes directrices autour de cette pratique?

Telastyn
la source
1
Dans la POO, il existe de nombreuses exceptions, mais au début, je considère cette mauvaise conception, surtout si toutes les classes partagent le même niveau d'accessibilité. Lorsque je lis un nom de classe, dans n'importe quel fichier, je veux savoir dans quel espace de noms la classe se trouve immédiatement sans avoir à se référer aux directives de l'espace de noms.
Leopold Asperger
Il existe un cas valable pour avoir plusieurs objets liés qui traitent du même état, mais ils sont généralement séparés par fonction. Vous avez peut-être un objet qui adapte un modèle à une vue. Un autre pourrait effectuer un calcul sur l'objet. Un autre pourrait être un adaptateur ou une façade. Donc , vous pourriez avoir FooAdapter, FooViewer, FooCalculator, etc. , mais certainement pas nommé la même chose. Sans plus d'informations sur les classes spécifiques dont vous parlez, il est difficile de fournir une réponse, cependant.
@JohnGaughan - considérez Employeecomme l'exemple. Il y a un employé qui est pour les autres employés, un pour les RH, un pour l'exposition extérieure. Ils sont tous nommés «employé» et ils ont tous des variétés d'aides (EmployeeRepository, EmployeeView, etc.). Ils sont tous dans la même DLL, et bien qu'ils partagent certaines propriétés communes (nom, titre), ils diffèrent tous (numéro de téléphone, salaire).
Telastyn
Ayant travaillé avec des systèmes qui modélisent Employeeplusieurs fois, il semble que vous ayez besoin d'un modèle avec l'union de tous les attributs et d'inclure un attribut typeor department. Sinon, il y a duplication inutile de code.
5
N'était-ce pas l'une des raisons pour lesquelles les espaces de noms ont commencé? Pour autoriser les collisions de noms?
Dan Pichelman

Réponses:

5

Je vais essayer de donner une réponse contre une telle utilisation, après avoir vu et travaillé sur cela dans l'un de mes projets également.

Lisibilité du code

Tout d'abord, considérez que la lisibilité du code est importante et cette pratique va à l'encontre de cela. Quelqu'un lit un morceau de code et disons simplement qu'il a une fonction doSomething(Employee e). Ce n'est plus lisible, car en raison de 10 Employeeclasses différentes existant dans différents packages, vous devrez d'abord recourir à la déclaration d'importation pour savoir ce que votre entrée est vraiment.

Cependant, c'est une vue de haut niveau et nous avons souvent des conflits de noms apparemment aléatoires, dont personne ne se soucie ni ne trouve, car la signification peut être dérivée du code restant et du paquet dans lequel vous vous trouvez. On pourrait même argumenter que, localement, il n'y a pas de problème, car bien sûr si vous voyez Employeedans un hrpackage vous devez savoir que nous parlons d'une vision RH de l'employé.

Les choses se désagrègent dès que vous quittez ces colis. Une fois que vous travaillez dans un module / package / etc différent et que vous devez travailler avec un employé, vous sacrifiez déjà la lisibilité si vous ne qualifiez pas complètement son type. De plus, avoir 10 Employeeclasses différentes signifie que l'auto-complétion de votre IDE ne fonctionnera plus et vous devez choisir manuellement le type d'employé.

Duplication de code

En raison de la nature même de chacune de ces classes étant liées les unes aux autres, votre code est susceptible de se détériorer en raison de nombreuses duplications. Dans la plupart des cas, vous aurez quelque chose comme le nom d'un employé ou un numéro d'identification, que chacune des classes doit implémenter. Même si chaque classe ajoute son propre type de vue, si elles ne partagent pas les données sous-jacentes des employés, vous vous retrouverez avec d'énormes masses de code inutile, mais coûteux.

Complexité du code

Qu'est-ce qui peut être si complexe que vous pourriez demander? Après tout, chacune des classes peut rester aussi simple qu'elle le souhaite. Ce qui devient vraiment un problème, c'est la façon dont vous propagez les changements. Dans un logiciel raisonnablement riche en fonctionnalités, vous pourrez peut-être modifier les données des employés - et vous souhaitez les refléter partout. Dis qu'une femme vient de se marier et que tu dois la renommer de XàYà cause de ça. Assez difficile pour faire ça partout, mais encore plus difficile quand vous avez toutes ces classes distinctes. Selon vos autres choix de conception, cela peut facilement signifier que chacune des classes doit implémenter son propre type d'écouteur ou tel pour être averti des modifications - ce qui se traduit essentiellement par un facteur appliqué au nombre de classes que vous devez traiter . Et plus de duplication de code bien sûr, et moins de lisibilité du code, .. Des facteurs comme celui-ci peuvent être ignorés dans l'analyse de complexité, mais ils sont certainement ennuyeux lorsqu'ils sont appliqués à la taille de votre base de code.

Communication de code

Outre les problèmes de complexité du code ci-dessus, qui sont également liés aux choix de conception, vous réduisez également la clarté de votre conception de niveau supérieur et perdez la capacité de communiquer correctement avec les experts du domaine. Lorsque vous discutez d'architecture, de conceptions ou d'exigences, vous n'êtes plus libre de faire des déclarations simples comme given an employee, you can do .... Un développeur ne saura plus ce que employeesignifie vraiment à ce stade. Bien qu'un expert en domaine le sache bien sûr. Nous faisons tous. sorte de. Mais en termes de logiciel, il n'est plus si facile de communiquer.

Comment se débarrasser du problème

Après avoir pris conscience de cela et si votre équipe convient qu'il s'agit d'un problème suffisamment important pour être résolu, vous devrez trouver un moyen de le résoudre. En règle générale, vous ne pouvez pas demander à votre manager de donner une semaine de congé à toute l'équipe afin qu'ils puissent sortir les poubelles. Donc, en substance, vous devrez trouver un moyen d'éliminer partiellement ces classes, une à la fois. La partie la plus importante à ce sujet est de décider - avec toute l'équipe - ce qu'est Employeevraiment un . N'intégrez pas tous les attributs individuels en un seul employé divin. Pensez davantage à un employé de base et, surtout, décidez où cette Employeeclasse résidera.

Si vous effectuez des révisions de code, il est particulièrement facile de vous assurer que le problème ne s'aggrave pas au moins, c'est-à-dire d'arrêter tout le monde dans sa piste lorsqu'il souhaite en ajouter un autre Employee. Veillez également à ce que les nouveaux sous-systèmes adhèrent à l'accord Employeeet ne soient pas autorisés à accéder aux anciennes versions.

Selon votre langage de programmation, vous pouvez également vouloir marquer les classes qui seront finalement éliminées avec quelque chose comme @Deprecatedpour aider votre équipe à réaliser immédiatement qu'elles travaillent avec quelque chose qui devra être changé.

En ce qui concerne l'élimination des classes d'employés obsolètes, vous pouvez décider pour chaque cas individuel de la meilleure façon de les éliminer, ou simplement convenir d'un modèle commun. Vous pouvez apposer le nom de la classe et l'enrouler autour du véritable employé, vous pouvez utiliser des modèles (décorateur ou adaptateur me viennent à l'esprit), ou, ou ou.

Pour faire court: cette "pratique" est techniquement solide, mais elle est étouffée par des coûts cachés qui ne se produiront que plus loin. Bien que vous ne puissiez pas vous débarrasser immédiatement du problème, vous pouvez commencer immédiatement à contenir ses effets nocifs.

Franc
la source
Avoir une classe Core semble être une bonne solution. D'autres classes peuvent en découler. À propos de l'IDE, la complétion automatique est suffisamment intelligente pour savoir quelle classe vous suggérer pour mieux correspondre au contexte. Dans l'ensemble, je ne vois pas pourquoi c'est un si gros problème, surtout si la base de code est utilisée en interne.
InformedA
1
Ce n'est pas assez intelligent une fois que vous êtes en dehors d'un contexte. Prenons une classe, qui a en fait besoin des deux classes problématiques - la saisie semi-automatique n'aidera pas du tout. Et cela ne les différenciera même pas des dix autres classes. Mais le vrai problème est ce nouveau membre de l'équipe, qui ne sait même pas ce que sont réellement tous ces domaines de package et lequel il devrait choisir.
Frank
Si plusieurs classes portant le même nom sont utilisées dans le même contexte, c'est difficile. Je n'ai pas vu ce cas. J'ai vu plusieurs classes avec le même nom, mais elles ne sont pas souvent utilisées dans le même contexte que vous l'avez mentionné.
InformedA
@randomA - malheureusement, ce cas est assez courant dans cette base de code.
Telastyn
De bons points, mais vous n'avez pas vraiment donné de solution au problème central, seulement une méthodologie une fois que le problème central est résolu ("ce qu'est réellement un employé"). La seule "solution" évidente ("intégrer tous les attributs individuels en un seul employé divin") que vous avez écartée, à juste titre. Supposons que vous ayez DB.Employee, Marshalling.Employee, ViewModels.Employee, GUI.Views.Employee, etc., quelle est votre solution?
Pablo H
9

Le Scala Collection Framework en est un bon exemple. Il existe des collections mutables et immuables ainsi que des collections en série et parallèles (et peut-être à l'avenir également distribuées). Il y a donc, par exemple, quatre Maptraits:

scala.collection.Map
scala.collection.immutable.Map
scala.collection.mutable.Map
scala.collection.concurrent.Map

plus trois ParMaps:

scala.collecion.parallel.ParMap
scala.collecion.parallel.immutable.ParMap
scala.collecion.parallel.mutable.ParMap

Encore plus intéressant:

scala.collection.immutable.Map            extends scala.collection.Map
scala.collection.mutable.Map              extends scala.collection.Map
scala.collection.concurrent.Map           extends scala.collection.mutable.Map

scala.collecion.parallel.ParMap           extends scala.collection.Map
scala.collecion.parallel.immutable.ParMap extends scala.collecion.parallel.ParMap
scala.collecion.parallel.mutable.ParMap   extends scala.collecion.parallel.ParMap

Ainsi, ParMaps'étend ParMaps'étend Mapet Maps'étend Maps'étend Map.

Mais avouons-le: comment les appelleriez- vous? Cela est parfaitement logique. C'est à cela que servent les espaces de noms!

Jörg W Mittag
la source
3
"C'est à ça que servent les espaces de noms!" => +1
svidgen
J'aime cela, mais dans le monde C # /. NET lorsque j'ai déplacé des classes parallèles dans un sous-espace de noms parallèle, cela se heurte alors à la classe d'assistance System.Threading.Parallel, que j'utilise beaucoup pour obtenir le parallélisme. Je ne voulais pas utiliser l'espace de noms 'Concurrent' à la place car cela est déjà utilisé pour signifier 'accès simultané' sur les classes de collection. Comme compromis, j'ai opté pour le sous-espace de noms 'Parallelized'.
redcalx
2

Il s'agit d'une question vraiment intéressante où les deux réponses essentiellement opposées sont toutes les deux correctes. Voici un exemple concret de cette discussion que nous avons sur un de mes projets.

Je pense qu'il y a jusqu'à présent deux considérations très importantes qui vous donneraient envie d'aller dans un sens ou dans l'autre, en s'inspirant des deux réponses de @Jorg et @Frank:

  1. Si vous prévoyez une situation où plusieurs des classes portant le même nom pourraient devoir être utilisées dans le même contexte de code, c'est une raison pour ne pas utiliser le même nom. Autrement dit, si vous avez besoin de travailler avec hr.Employeemais en même temps que vous devez regarder les autorisations via security.Employee, cela va devenir très ennuyeux très rapidement.
  2. Inversement, si vos classes portant le même nom ont toutes des API identiques ou très similaires, c'est un bon exemple de cas où vous devriez utiliser le même nom avec des espaces de noms différents. L'exemple Scala est précisément celui-ci, où toutes les Mapclasses implémentent la même interface ou sont des sous-classes les unes des autres. Dans ce cas, l'ambiguïté ou la "confusion" peut être considérée comme une bonne chose, car l'utilisateur peut à juste titre traiter toutes les implémentations de la classe ou de l'interface de la même manière ... ce qui est le point d'interfaces et de sous-classes.
S'pht'Kr
la source