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 la ligne directrice - 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 du 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 arrière-plans d’autres langues. Il existe également d'autres exceptions, comme le Base
suffixe assez largement utilisé pour désigner une classe abstraite - comme avec une interface, vous savez que le type est supposé être polymorphe.
Il existe également une fonctionnalité conviviale permettant de laisser le nom non préfixé par I pour les fonctionnalités liées à cette interface sans avoir à recourir à une classe abstraite (ce qui serait préjudiciable en raison du manque de classe à héritage multiple en C #). Cela a été popularisé par LINQ, qui utilise IEnumerable<T>
comme interface et Enumerable
comme référentiel de méthodes s’appliquant à cette interface. Cela n’est pas nécessaire en Java, où les interfaces peuvent également contenir des implémentations de méthodes.
En fin de compte, le I
préfixe 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 directives 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
est la notion abstraite de banane. S'il peut y avoir une classe d'implémentation qui n'aurait pas de meilleur nom que Banana
, l'abstraction n'a aucun sens, et vous devriez abandonner l'interface et simplement utiliser une classe. S'il existe un meilleur nom (par exemple, LongBanana
ou AppleBanana
), il n'y a aucune raison de ne pas utiliser Banana
le nom de l'interface. Par conséquent, l’utilisation du I
préfixe signifie que vous avez une abstraction inutile, ce qui rend le code plus difficile à comprendre sans aucun avantage. Et comme la POO stricte vous oblige toujours à coder contre des interfaces, le seul endroit où vous ne verriez pas le I
préfixe d'un type serait un constructeur - un bruit inutile.
Si vous appliquez cela à votre exemple d' IParser
interface, vous pouvez clairement voir que l'abstraction est entièrement dans le territoire "sans signification". Soit il y a quelque chose de spécifique sur une mise en œuvre 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 # I
, conservez-le , à moins que votre base de code n'omette déjà le préfixe -prefix. Notez simplement chaque fois que vous voyez du code class Something: ISomething
- cela signifie que quelqu'un n'est pas très bon 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.