Problèmes de dénomination: Faut-il renommer "Quelque chose"?

Le chapitre d'Oncle Bob sur les noms dans Clean Code vous recommande d'éviter les encodages dans les noms, principalement en ce qui concerne la notation hongroise. Il mentionne également spécifiquement la suppression du préfixe I des interfaces, mais n'en montre pas d'exemples.

Supposons ce qui suit:

  • L’utilisation de l’interface est principalement destinée à permettre la testabilité grâce à l’injection de dépendance
  • Dans de nombreux cas, cela conduit à avoir une seule interface avec un seul implémenteur

Ainsi, par exemple, comment nommer ces deux personnes? Analyseur et ConcreteParser ? Parser et ParserImplementation ?

public interface IParser {
    string Parse(string content);
    string Parse(FileInfo path);
}

public class Parser : IParser {
    //Implementations
}

Ou devrais-je ignorer cette suggestion dans les cas de mise en œuvre unique comme celui-ci?

67
Cela n'en fait pas moins une religion. Tu dois chercher des raisons, pas seulement quelqu'un X dit Y.
ajouté l'auteur Seb Nilsson, source
D'accord. De temps en temps, quelqu'un marche sur une caisse à savon et déclare que telle chose si populaire est mauvaise. Je l'ai même fait, mais au moins j'ai expliqué pourquoi, afin que les gens puissent voir par eux-mêmes s'ils l'achètent ou non.
ajouté l'auteur Seb Nilsson, source
@VinkoVrsalovic: FWIW, voici un exemple de ma caisse à savon .
ajouté l'auteur Seb Nilsson, source
J'ai l'opinion que le langage devrait appliquer la convention de nommage
ajouté l'auteur orjan, source
@MikeDunlavey Le code propre de l'oncle Bob date de 2009.
ajouté l'auteur wvdschel, source
La règle de majorité @TripeHound n'est pas toujours la meilleure option. S'il y a des raisons pour lesquelles certaines choses sont meilleures que d'autres, une seule personne peut convaincre le reste de l'équipe de défendre ses arguments. Se soumettre aveuglément à la majorité sans évaluer les choses à travers conduit à la stagnation. Je vois dans les réponses que, dans ce cas concret, il n’ya aucune bonne raison de retirer l’Is, mais cela n’annule pas avoir demandé d’abord.
ajouté l'auteur wvdschel, source
@ MikeDunlavey Et c'est la raison de la question!
ajouté l'auteur wvdschel, source
@MikeDunlavey Pour être juste avec Oncle Bob, il explique les raisons pour lesquelles il a évité la notation hongroise, c'est simplement le retrait du "I" des interfaces qu'il n'a pas suffisamment expliqué à mon avis.
ajouté l'auteur wvdschel, source
Pas vraiment. Dans un modèle d'interface utilisateur déclaratif, une partie de l'intention est de séparer le type de données de la présentation.
ajouté l'auteur 조남진, source
Un moyen facile de résoudre votre confusion. Jetez ce livre à la poubelle et parlez à votre équipe.
ajouté l'auteur 조남진, source
Et comme note, la notation hongroise est souvent utilisée pour nommer les contrôles. (Ce n'est pas aussi populaire avec les modèles d'interface utilisateur déclarative tels que WPF, mais il est encore très courant dans des endroits comme WinForms.)
ajouté l'auteur 조남진, source
Ce n'est pas possible sans limiter ce que vous pouvez nommer des classes et des variables.
ajouté l'auteur 조남진, source
Un développeur C# qui se perd parce qu'il voit un nom d'interface qui ne commence pas par "I" est une personne qui n'a pas besoin d'être un développeur C #.
ajouté l'auteur user1172763, source
@MatthewWhited L'ironie étant que la plupart de ces utilisations sont des systèmes hongrois, alors que les applications hongroises seraient probablement meilleures.
ajouté l'auteur JAB, source
Je ne suis absolument pas d'accord avec l'hypothèse selon laquelle les interfaces sont principalement utilisées pour permettre la testabilité via l'injection de dépendance.
ajouté l'auteur axl, source
Au pays de PHP, les interfaces ont été traditionnellement identifiées par un suffixe Interface. Il existe un groupe de normes assez performant qui a publié trois normes utilisant la convention de suffixe. Mais maintenant, ils ont été suivis de côté sur ce qui est la "bonne" voie pour les nouvelles normes. En gros, vous pouvez lire la même discussion ici: groups.google.com/forum/#! topic/php-fig/Potawlu2CrQ Certaines choses ne changent jamais.
ajouté l'auteur Cerad, source
S'il existe un corps de code existant (que vous n'allez pas réécrire), respectez la convention utilisée. Si un nouveau code est utilisé, utilisez ce que la majorité des personnes qui vont y travailler sont plus heureuses avec/ont l'habitude de le faire. Ce n'est que si ni ce qui précède ne s'applique que cela devrait devenir une question philosophique.
ajouté l'auteur user128766, source
En fait, dans la programmation Android (y compris Java), la convention IInterface est tout aussi bien établie qu’en C #. Je vote aussi pour avoir ignoré le livre sur ce sujet (aussi).
ajouté l'auteur jsageryd, source
Le livre de "Uncle Bob" se concentre sur JAVA, pas sur C #. La plupart des paradigmes sont les mêmes avec C#, mais certaines conventions de dénomination diffèrent (regardez également les noms des fonctions lowerCamelCase en Java). Lorsque vous écrivez en C#, utilisez les conventions de dénomination C #. Quoi qu’il en soit, suivez l’essentiel de son chapitre sur la dénomination: des noms lisibles qui communiquent le sens de l’article!
ajouté l'auteur Boris Nikolov, source

8 Réponses

Bien que beaucoup, y compris "Uncle Bob", conseillent de ne pas utiliser I comme préfixe pour les interfaces, il s'agit d'une tradition bien établie avec C #. En termes généraux, cela devrait être évité. Mais si vous écrivez en C#, vous devriez vraiment suivre les conventions de ce langage et l'utiliser. Ne pas le faire provoquera une énorme confusion avec les personnes connaissant C# et essayant de lire votre code.

190
ajouté
Vous ne fournissez aucune base pour "En termes généraux, cela devrait être évité". En Java, je devrais être évité. En C#, il devrait être adopté. Les autres langues suivent leurs propres conventions.
ajouté l'auteur Fr., source
Les commentaires ne sont pas pour une discussion prolongée; cette conversation a été déplacée vers discuter .
ajouté l'auteur maple_shaft, source
Le principe "en termes généraux" est simple: un client devrait avoir le droit de ne pas savoir s'il parle à une interface ou à une implémentation. Les deux langues se sont trompées. C# s'est trompé dans la convention de nommage. Java s'est trompé de bytecode. Si je bascule une implémentation sur une interface portant le même nom en Java, je dois recompiler tous les clients, même si le nom et les méthodes n'ont pas changé. Ce n'est pas un "choisissez votre poison". Nous venons de faire les choses mal. S'il vous plaît apprendre de cela si vous créez une nouvelle langue.
ajouté l'auteur candied_orange, source

L'interface est le concept logique important; par conséquent, l'interface doit porter le nom générique. Donc, je préfère avoir

interface Something
class DefaultSomething : Something
class MockSomething : Something

que

interface ISomething
class Something : ISomething
class MockSomething : ISomething

Ce dernier a plusieurs aspects:

  1. Quelque chose n’est qu’une implémentation d’ISomething, mais c’est celle qui porte le nom générique, comme si c’était quelque chose de spécial.
  2. Mock Quelque chose semble provenir de Quelque chose, mais implémente ISomething. Cela devrait peut-être s'appeler MockISomething, mais je ne l'ai jamais vu dans la nature.
  3. Le refactoring est plus difficile. Si Quelque chose est une classe maintenant et que vous ne découvrez que plus tard qu'il est nécessaire d'introduire une interface, cette interface doit porter le même nom, car elle doit être transparente pour les clients, qu'il s'agisse d'une classe concrète, d'une classe abstraite ou d'une interface. . Quelque chose casse ça.
38
ajouté
Pourquoi ne pas suivre la norme bien établie fournie par C #? Quel avantage cela confère-t-il au-delà du coût de confusion et d'irritation pour chaque programmeur C# qui vous suit?
ajouté l'auteur sgwill, source
Si quelque chose est standard, cela ne signifie pas que c'est le choix le plus logique. C'est comme la politique, où un groupe de personnes vient collectivement d'accepter de choisir le choix et que tout le monde est censé suivre. Si vous avez de bonnes raisons et que vous êtes assez courageux, vous pouvez toujours créer un autre parti politique.
ajouté l'auteur Pratyush, source
Je ne vois aucun problème à avoir une classe instanciable avec le même nom qu'une interface si la grande majorité des instances qui implémentent l'interface seront cette classe. Par exemple, beaucoup de code nécessite une implémentation directe et simple de IList et ne tient pas vraiment à la façon dont il est stocké en interne; pouvoir lopper le I et avoir un nom de classe utilisable semble plus agréable que de devoir savoir par magie (comme en Java) que le code souhaite une implémentation ordinaire de List devrait utiliser ArrayList .
ajouté l'auteur supercat, source
C’est un mauvais conseil pour C#, bien mieux de suivre les conventions existantes. Quand à Rome et tout ça.
ajouté l'auteur Andy, source
Je suis d'accord avec vous, mais si vous empruntez cette route, vous devrez vous battre chaque linter jamais fait pour C #.
ajouté l'auteur RubberDuck, source
Préfixe ou postfixe est une question de goût. OMI, chaque implémentation doit avoir une raison d'exister et cette raison doit être reflétée dans le nom. Ainsi, si vous avez des référentiels en SQL, texte brut et en mémoire (pour les tests unitaires), les noms doivent être RepositorySQL (ou SqlRepository), RepositoryCSV, RepositoryInMemory.
ajouté l'auteur Dinesh, source
@ArtB Quelques raisons. Premièrement, il n'y a pas de langage où la convention est un joli marqueur court sur la classe comme CFoo: Foo . Donc, cette option n'est pas vraiment disponible. Deuxièmement, vous obtenez une étrange incohérence, car certaines classes ont le marqueur et d’autres pas, selon qu’il existe d’autres implémentations de la même interface. CFoo: Foo mais SqlStore: Store . C’est l’un des problèmes que j'ai avec Impl , qui est essentiellement un marqueur plus laid. Peut-être que s'il y avait une langue avec la convention de toujours ajoutant un C (ou autre), cela pourrait être meilleur que I
ajouté l'auteur Ben Aaronson, source
@wallenborn C'est bien, mais de manière réaliste, vous n'avez souvent qu'une seule implémentation légitime d'une interface, car il est courant d'utiliser des interfaces pour la mocabilité dans les tests unitaires. Je ne souhaite pas insérer Par défaut ou Real ou Impl dans un si grand nombre de noms de classe.
ajouté l'auteur Ben Aaronson, source
Je suis d’accord avec vous pour appeler la première classe concrète le nom générique. Bien que je puisse postfixer plutôt que de préfixer le nom: RepositorySql etc.
ajouté l'auteur Ewan, source
@BenAaronson Oui, "Impl" est un marqueur plus laid, mais n'utilisez ces identifiants qu'une ou deux fois dans une application Java Spring, mais l'interface coûte des centaines de fois. La moindre laideur globale IMHO.
ajouté l'auteur ProudNoob, source
@BenAaronson, pourquoi voudriez-vous que le marqueur de type de l'interface soit visible partout, par opposition au nom de la classe que vous ne verrez qu'une ou deux fois (c'est-à-dire la classe elle-même et lorsque vous l'instanciez dans le cadre d'injection que vous êtes en utilisant).
ajouté l'auteur ProudNoob, source

Ce n'est pas juste des conventions de nommage. C# ne prenant pas en charge l'héritage multiple, cette utilisation héritée de la notation hongroise présente un petit avantage, quoique utile, lorsque vous héritez d'une classe de base et implémentez une ou plusieurs interfaces. Donc ça...

class Foo : BarB, IBarA
{
   //Better...
}

... est préférable à cela ...

class Foo : BarB, BarA
{
   //Dafuq?
}

OMI

27
ajouté
Il est à noter que Java évite nettement ce problème en utilisant des mots-clés différents pour l'héritage de classe et l'implémentation d'interface, à savoir hérite de vs implémentations .
ajouté l'auteur Mark Ransom, source
@Andy C’est une pollution en ce sens qu’elle n’offre aucune information donnant lieu à une action (sauf peut-être à l’endroit mentionné dans cette réponse, mais comme vous dites, pas même là). C'est donc un caractère complètement inutile dans le nom. Le seul avantage potentiel de savoir qu’un type est une interface (ce qui est un avantage assez discutable au départ) est compromis, car la convention n’est pas appliquée de manière statique.
ajouté l'auteur Mark Ransom, source
@Andy L'inconvénient de votre argument est que, plutôt que de garder le désagrément contenu, vous polluez maintenant toute votre base de code avec. Les mots-clés ont évidemment aussi le (petit) avantage d’imposer une utilisation correcte via le compilateur; pour la notation hongroise, rien ne vous empêche de préfixer à tort un nom de classe avec “ I ” et de l'oublier devant les noms d'interface - ce qui est exactement le problème du hongrois: c'est une convention non appliquée, c'est-à-dire un typage faible . Un gros problème? Certainement pas. Mais ni l'un ni l'autre ne sont deux mots-clés contextuels.
ajouté l'auteur Mark Ransom, source
@ OrangeDog Yup.
ajouté l'auteur Mark Ransom, source
@Andy La valeur ajoutée est que nous pouvons éviter la notation hongroise, tout en préservant toute la clarté présentée dans cette réponse.
ajouté l'auteur Mark Ransom, source
Il ne devrait vraiment y avoir aucune confusion; si vous héritez d'une classe, elle DOIT être en premier dans la liste, avant la liste des interfaces implémentées. Je suis à peu près sûr que cela est imposé par le compilateur.
ajouté l'auteur Andy, source
@ KonradRudolph Je ne considère pas la pollution comme une sorte de pollution; Je viens de dire que je n'ai vraiment pas de problème avec IInterface. Et cette réponse est un peu fausse aussi, maintenant que j'y pense.
ajouté l'auteur Andy, source
@ KonradRudolph Dans ce cas, je ne pense pas qu'être plus verbeux ajoute vraiment une valeur ici.
ajouté l'auteur Andy, source
@ KonradRudolph Je préfèrerais simplement préfixer I; Je n'ai pas vraiment de problème avec la notation hongroise que je voudrais taper implémente/s'étend partout où j'aurais pu taper un deux-points simplement pour éviter la notation hongroise sur les noms d'interface.
ajouté l'auteur Andy, source
@ KonradRudolph Comme je l'ai dit, je ne me soucie pas d'un seul caractère supplémentaire dans le nom, et cela ne me dérange pas que ce ne soit pas une action, surtout si cela signifie que je peux nommer la mise en œuvre réelle Parser au lieu de devoir inventer quelque chose le nom de la classe car l'interface n'a pas de préfixe I.
ajouté l'auteur Andy, source
@RobbieDee Il sera également coloré différemment dans l'éditeur.
ajouté l'auteur Andy, source
@Andy ... si vous héritez d'une classe, elle DOIT être la première de la liste C'est génial, mais sans le préfixe, vous ne sauriez pas si vous avez affaire à deux interfaces ou à une classe de base et une interface ...
ajouté l'auteur Robbie Dee, source
@Andy Cela dépend de votre éditeur qui n'a rien à voir avec la syntaxe.
ajouté l'auteur Robbie Dee, source
@ KonradRudolph, vous voulez dire extend .
ajouté l'auteur OrangeDog, source
@Andy En mettant la classe en premier ( classe Foo: BarA, BarB ), vous pouvez dire que BarB est bien une interface, mais il y a toujours une ambiguïté quant à savoir si BarA en est un aussi - ce pourrait être une classe de base. En utilisant la convention de dénomination, l'ambiguïté est supprimée ( classe Foo: BarA, IBarB vs classe Foo: IBarA, IBarB ).
ajouté l'auteur monevus3, source

Non non Non.

Les conventions de dénomination ne sont pas une fonction de votre fandom Oncle Bob. Ils ne sont pas fonction du langage, C# ou autre.

Ils sont une fonction de votre base de code. Dans une moindre mesure de votre magasin. En d'autres termes, le premier dans la base de code définit la norme.

C'est seulement à ce moment-là que vous décidez. Après cela, soyez juste cohérent. Si quelqu'un commence à être incohérent, retirez son arme à feu nerf et faites-lui mettre à jour la documentation jusqu'à ce qu'il se repente.

Lorsque je lis une nouvelle base de code si je ne vois jamais de préfixe I , je vais bien, quelle que soit la langue. Si j'en vois un sur chaque interface, je vais bien. Si j'en vois parfois, quelqu'un va payer.

Si vous vous trouvez dans le contexte raréfié de l'établissement de ce précédent pour votre base de code, je vous prie instamment de considérer ceci:

En tant que client, je me réserve le droit de ne pas se soucier de quoi je parle. Tout ce dont j'ai besoin, c'est d'un nom et d'une liste de choses que je peux appeler contre ce nom. Ceux-ci ne peuvent pas changer à moins que je le dise. Je possède cette partie. Ce dont je parle, interface ou pas, n’est pas mon département.

Si vous insérez un I dans ce nom, le client s'en fiche. S'il n'y a pas de I , le client s'en fiche. Ce à quoi il parle peut être concret. Il se peut que non. Puisqu'il n'appelle pas new , cela ne fait aucune différence. En tant que client, je ne sais pas. Je ne veux pas savoir.

Maintenant, en tant que singe de code regardant ce code, même en Java, cela pourrait être important. Après tout, si je dois remplacer une implémentation par une interface, je peux le faire sans en informer le client. S'il n'y a pas de tradition I , je ne pourrais même pas le renommer. Ce qui pourrait être un problème parce que maintenant nous devons recompiler le client car même si la source ne se soucie pas du binaire. Quelque chose de facile à manquer si vous n'avez jamais changé le nom.

Peut-être que ce n'est pas un problème pour vous. Peut être pas. Si c'est le cas, c'est une bonne raison de s'en soucier. Mais pour l'amour des jouets de bureau, n'allez pas prétendre que nous devrions le faire à l'aveuglette, car c'est la même chose dans certaines langues. Soit eu une sacrée bonne raison ou ne s'en soucie pas.

Il ne s'agit pas de vivre avec pour toujours. Il s’agit de ne pas me faire chier au sujet de la base de code qui ne correspond pas à votre mentalité particulière à moins que vous ne soyez prêt à résoudre le «problème» partout. À moins que vous n'ayez une bonne raison de ne pas prendre le chemin de la moindre résistance à l'uniformité, ne me parlez pas de prêcher la conformité. J'ai de meilleures choses à faire.

15
ajouté
Si vous utilisez C#, vous verrez ces I s. Si votre code les utilise, vous les verrez partout. Si votre code ne les utilise pas, vous les verrez dans le code de la structure, ce qui conduit exactement au mélange que vous ne voulez pas.
ajouté l'auteur Allen Bargi, source
La cohérence est essentielle, mais dans un langage typé de manière statique et avec des outils de refactoring modernes, il est assez facile et sûr de changer de nom. Donc, si le premier développeur a fait un mauvais choix en nommant, vous ne devez pas vivre avec pour toujours. Mais vous pouvez changer le nom dans votre propre base de code, mais vous ne pouvez pas le changer dans le framework ou dans des bibliothèques tierces. Puisque le préfixe I (qu’il le veuille ou non) soit une convention établie dans le monde .net, il est préférable de la suivre que de ne pas la suivre.
ajouté l'auteur JacquesB, source

Vous avez raison de dire que cette convention I = interface enfreint les (nouvelles) règles

Auparavant, vous préfixiez tout avec son type intCounter boolFlag etc.

Si vous voulez nommer des choses sans le préfixe, mais aussi éviter "ConcreteParser", vous pouvez utiliser des espaces de noms.

namespace myapp.Interfaces
{
    public interface Parser {...
}


namespace myapp.Parsers
{
    public class Parser : myapp.Interfaces.Parser {...
}
8
ajouté
@luaan avez-vous vu les exemples que j'ai utilisés? Ont-ils l'air inutilisable? Nul besoin de choisir un exemple artificiellement long qui n’a aucun sens dans les langues modernes pour faire valoir votre point de vue. Je conviens que pour les "anciennes" langues, le hongrois a du sens, mais c’est ce que je voulais dire quand je dis que nous pouvons faire mieux maintenant, en utilisant des langues plus modernes. Si vous êtes coincé avec C, continuez à utiliser Apps Hungarian.
ajouté l'auteur wvdschel, source
@CodyGray Je suis d'accord avec la valeur de la notation hongroise, mais nous pouvons maintenant faire mieux que cela: utilisez des noms descriptifs réels, tels que CheckBoxXCoordinate, NameCharCount, IsCheckBoxVisible, FilePointer, WindowHandle, il n'est donc pas nécessaire d'utiliser cette notation car vars peut être plus descriptif. sans avoir à comprendre et à retenir une notation spécifique. NameCharCount + CheckBoxXCoordinate est encore plus manifestement faux que xControl + cchName
ajouté l'auteur wvdschel, source
Nouveau? vieux? Attendez, je pensais que la programmation était plus une affaire de rationalité que de battage publicitaire. Ou était-ce au bon vieux temps?
ajouté l'auteur Nick Gebbie, source
"Dans l'ancien temps", les compilateurs étaient souvent sévèrement limités en longueur d'identificateur; quelque chose comme 6-10 caractères était commun. Vous pouvez utiliser des noms plus longs, mais la partie au-delà des premiers caractères n'a pas été prise en compte pour l'unicité des noms de variable. Je pense au moins dans Turbo C 2.0, la longueur était même configurable!
ajouté l'auteur Pacane, source
Je pense que vous pensez à la notation hongroise, dans laquelle vous avez bien préfixé des noms avec des types. Mais il n’ya eu aucun moment où vous écriviez quelque chose comme intCounter ou boolFlag . Ce sont les mauvais "types". A la place, vous écrirez pszName (pointeur sur une chaîne contenant un nom contenant NUL), cchName (nombre de caractères dans la chaîne "name"), xControl (coordonnée x du contrôle), fVisible (indicateur indiquant que quelque chose est visible), pFile (pointeur sur un fichier), hWindow (gérer une fenêtre), etc.
ajouté l'auteur Ricky, source
Il y a encore beaucoup de valeur dans cela. Un compilateur ne détectera pas l'erreur lorsque vous ajoutez xControl à cchName . Ils sont tous deux de type int , mais ils ont une sémantique très différente. Les préfixes rendent cette sémantique évidente pour un humain. Un pointeur sur une chaîne terminée par NUL ressemble exactement à un pointeur sur une chaîne de style Pascal pour un compilateur. De même, le préfixe I pour les interfaces est apparu dans le langage C ++ où les interfaces ne sont en réalité que des classes (et même pour le C, où il n’existe pas de classe ou d’interface au niveau du langage, c’est tout. juste une convention).
ajouté l'auteur Ricky, source
@ JoshuaTaylor, merci d'avoir lié cet article.
ajouté l'auteur Wildcard, source
Je tiens à signaler que je ne recommande pas actuellement de ne pas préfixer les interfaces avec I., mais que si vous souhaitez supprimer le préfixe, les espaces de noms valent mieux que de suffixer la mise en œuvre avec 'Concrete'.
ajouté l'auteur Ewan, source
les bons vieux jours tu veux dire, avant tout ce longVariableNamesAreGoodForSomeReason nononsense! (ou gdOD -beforeNsense comme je l'appelle)
ajouté l'auteur Ewan, source
il ne s'agit pas uniquement de composer des conventions et de bloguer sur la manière dont elles «améliorent la lisibilité»
ajouté l'auteur Ewan, source
@VinkoVrsalovic Dans certains cas, oui. Mais écrivez-vous réellement des noms tels que FirstnameLongPointerToANullTerminatedString ? La notation hongroise sémantique est toujours très utile, vous devez simplement vous assurer que cela vaut la peine d’enseigner le sens de la signification - vous aurez besoin de faire beaucoup d’enseignement pour tout nouveau venu de toute façon, ne prétendez pas simplement Comme par magie, suivez les mêmes directives que vous, même si vous les basez toutes les deux sur le même document. Un typage strict serait bien sûr préférable dans la plupart des cas, raison pour laquelle le hongrois n'est pas tellement utilisé dans des langages tels que C# et le C ++ moderne.
ajouté l'auteur Luaan, source
@CodyGray Joel Spolsky a écrit un bel article, Rendre le code erroné et corrigé à propos de la notation hongroise et la façon dont il a été (mal) compris. Il a un avis similaire: le préfixe devrait être un type de niveau supérieur, pas le type de stockage de données brutes. Il utilise le mot "genre", expliquant "J'utilise le mot genre exprès, parce que Simonyi a utilisé à tort le mot type dans son document. , et des générations de programmeurs ont mal compris ce qu'il voulait dire. "
ajouté l'auteur Joshua Taylor, source

Il y a une toute petite différence entre Java et C# qui est pertinente ici. En Java, chaque membre est virtuel par défaut. En C#, chaque membre est scellé par défaut - à l'exception des membres d'interface.

Les principes qui accompagnent cette influence ont une influence sur les lignes directrices - en Java, tout type public doit être considéré comme non final, conformément au principe de substitution de Liskov [1]. Si vous n'avez qu'une seule implémentation, vous nommerez la classe Parser ; Si vous constatez que vous avez besoin de plusieurs implémentations, il vous suffira de changer la classe pour une interface portant le même nom et de renommer l'implémentation concrète en quelque chose de descriptif.

En C#, l'hypothèse principale est que, lorsque vous obtenez une classe (le nom ne commence pas par I ), c'est la classe que vous voulez. Remarquez que cela n’est pas du tout fiable à 100% - un contre-exemple typique serait des classes telles que Stream (qui aurait vraiment dû être une interface, ou quelques interfaces), et chacun a ses propres directives. et des arrière-plans d'autres langues. Il existe également d'autres exceptions, comme le suffixe assez largement utilisé Base pour désigner une classe abstraite - comme avec une interface, vous savez que le type est supposé polymorphe.

There's also a nice usability feature in leaving the non-I-prefixed name for functionality that relates to that interface without having to resort to making the interface an abstract class (which would hurt due to the lack of class multiple-inheritance in C#). This was popularised by LINQ, which uses IEnumerable as the interface, and Enumerable as a repository of methods that apply to that interface. This is unnecessary in Java, where interfaces can contain method implementations as well.

En fin de compte, le préfixe I est largement utilisé dans le monde C# et, par extension, dans le monde .NET (étant donné que la majeure partie du code .NET est écrit en C#, il est logique de suivre les instructions de C# pour la plupart des interfaces publiques). Cela signifie que vous travaillerez presque certainement avec des bibliothèques et du code respectant cette notation. Il est donc logique d'adopter la tradition pour éviter toute confusion inutile - ce n'est pas comme si le préfixe rendrait votre code meilleur :)

Je suppose que le raisonnement d'oncle Bob ressemblait à ceci:

IBanana is the abstract notion of banana. If there can be any implementing class that would have no better name than Banana, the abstraction is entirely meaningless, and you should drop the interface and just use a class. If there is a better name (say, LongBanana or AppleBanana), there's no reason not to use Banana as the name of the interface. Therefore, using the I prefix signifies that you have a useless abstraction, which makes the code harder to understand with no benefit. And since strict OOP will have you always code against interfaces, the only place where you wouldn't see the I prefix on a type would be on a constructor - quite pointless noise.

Si vous appliquez cela à votre exemple d'interface IParser , vous pouvez clairement voir que l'abstraction est entièrement dans le territoire "sans signification". Soit il y a quelque chose de spécifique dans l’implémentation concrète d’un analyseur (par exemple, JsonParser , XmlParser , ...), ou vous devez simplement utiliser une classe. "L'implémentation par défaut" n'existe pas (bien que dans certains environnements, cela ait effectivement un sens, notamment COM), il existe une implémentation spécifique ou vous souhaitez une classe abstraite ou des méthodes d'extension pour les "valeurs par défaut". Cependant, en C#, conservez-le, à moins que votre base de code n'omette déjà le préfixe I . Notez simplement chaque fois que vous voyez un code comme class Something: IS quelque chose - cela signifie que quelqu'un n'est pas très doué pour suivre YAGNI et créer des abstractions raisonnables.

[1] - Techniquement, ceci n'est pas spécifiquement mentionné dans le papier de Liskov, mais c'est l'un des fondements du papier original de la POO et dans ma lecture de Liskov, elle n'a pas contesté cela. Dans une interprétation moins stricte (celle prise par la plupart des langages POO), cela signifie que tout code utilisant un type public destiné à être substitué (c'est-à-dire non final/scellé) doit fonctionner avec toute implémentation conforme de ce type.

8
ajouté
Nitpick: LSP ne dit pas que tous les types doivent être non finaux. Cela indique simplement que pour les types non finaux, les consommateurs doivent pouvoir utiliser les sous-classes de manière transparente. - Et bien sûr, Java a aussi des types finaux.
ajouté l'auteur Mark Ransom, source
@ KonradRudolph I ajouté une note de bas de page à ce sujet; Merci pour les commentaires :)
ajouté l'auteur Luaan, source

Ainsi, par exemple, comment nommer ces deux personnes? Analyseur et ConcreteParser? Parser et ParserImplementation?

Dans un monde idéal, vous ne devriez pas préfixer votre nom avec un raccourci ("I ..."), mais simplement laisser le nom exprimer un concept.

J'ai lu quelque part (et ne peux pas en trouver l'origine) l'opinion selon laquelle cette convention ISomething vous conduit à définir un concept "IDollar" lorsque vous avez besoin d'une classe "Dollar", mais la solution correcte serait un concept appelé simplement "devise".

En d’autres termes, la convention simplifie la tâche de nommer des choses et la solution de facilité est subtilement fausse .


Dans le monde réel, les clients de votre code (qui peut être simplement "vous du futur") doivent pouvoir le lire plus tard et le connaître le plus rapidement possible (ou avec le moins d'effort possible) (donnez aux clients de votre code ce qu’ils espèrent obtenir).

The ISomething convention, introduces cruft/bad names. That said, the cruft is not real cruft *), unless the conventions and practices used in writing the code are no longer actual; if the convention says "use ISomething", then it is best to use it, to conform (and work better) with other devs.


*) Je peux me permettre de dire que ceci parce que "cruft" n'est pas un concept exact de toute façon :)

4
ajouté

Comme le mentionnent les autres réponses, préfixer les noms d'interface avec I fait partie des instructions de codage du framework .NET. Etant donné que les classes concrètes interagissent plus souvent que les interfaces génériques en C# et VB.NET, elles sont de premier ordre dans les schémas de nommage et devraient donc avoir les noms les plus simples - d'où, IParser pour l'interface et Parser pour l'implémentation par défaut.

Mais dans le cas de Java et de langages similaires, où les interfaces sont préférées aux classes concrètes, Uncle Bob a raison de dire que le I doit être supprimé et que les interfaces doivent avoir les noms les plus simples - Parser pour votre interface d'analyse, par exemple. Mais au lieu d’un nom basé sur un rôle tel que ParserDefault , la classe devrait avoir un nom décrivant la manière dont l’analyseur est mis en œuvre :

public interface Parser{
}

public class RegexParser implements Parser {
   //parses using regular expressions
}

public class RecursiveParser implements Parser {
   //parses using a recursive call structure
}

public class MockParser implements Parser {
   //BSes the parsing methods so you can get your tests working
}

This is, for instance, how Set, HashSet, and TreeSet are named.

2
ajouté
Non ... Non. Ce n'est pas comme ça que la "vraie POO" fonctionne. En Java, vous devriez prendre le temps de sceller les méthodes qui ne devraient pas être ignorées, sans le laisser comme dans le Far West où tout le monde peut tout faire. LSP ne signifie pas que vous pouvez remplacer tout ce que vous voulez. LSP signifie que vous pouvez remplacer une classe par une autre en toute sécurité. Smh
ajouté l'auteur RubberDuck, source
Les membres sont scellés par défaut afin que nous soyons explicites sur ce que les héritiers peuvent remplacer par @Luaan. Certes, c'est parfois un PITA, mais ça n'a rien à voir avec une préférence pour les types concrets. Le virtuel n'est certainement pas un cas particulier en C #. On dirait que vous avez eu la malchance de travailler dans une base de code C# écrite par des amateurs. Je suis désolé que ce soit votre expérience avec la langue. Il est préférable de se rappeler que le dev n'est pas la langue et vice versa.
ajouté l'auteur RubberDuck, source
Les interfaces sont également préférées en C #. Qui vous a dit qu'il était préférable d'utiliser des types concrets dans dotnet?
ajouté l'auteur RubberDuck, source
@RubberDuck Hah, je n'ai jamais dit que je ne l'aimais pas. Je le préfère beaucoup, car la plupart des gens ne comprennent pas l'indirection . Même parmi les programmeurs. Scellé par défaut est mieux à mon avis. Et quand j'ai commencé avec C#, tout le monde était un amateur, y compris l'équipe .NET :) Dans "real OOP", tout est supposé être virtuel - chaque type doit être remplaçable par un type dérivé, devrait pouvoir annuler n'importe quel comportement. Mais "real OOP" a été conçu pour les spécialistes, et non pour les personnes qui ont cessé de remplacer des ampoules par de la programmation, car il leur fallait moins de travail pour plus d'argent :)
ajouté l'auteur Luaan, source
@RubberDuck C'est caché dans certaines des hypothèses que le langage vous impose. Par exemple, le fait que les membres soient scellés par défaut et que vous deviez les rendre explicitement virtuel . Etre virtuel est le cas particulier de C #. Bien sûr, étant donné que l'approche recommandée pour l'écriture de code a changé au fil des ans, de nombreuses reliques du passé devaient rester pour des raisons de compatibilité. L'important, c'est que si vous obtenez une interface en C# (qui commence par I ), vous savez que c'est un type totalement abstrait; Si vous obtenez une non-interface, l'hypothèse habituelle est que c'est le type que vous voulez, LSP être damné.
ajouté l'auteur Luaan, source