Je lisais " Clean Code " de Robert Martin pour devenir, je l' espère, un meilleur programmeur. Jusqu'à présent, rien de tout cela n'a été réellement novateur, mais cela m'a fait penser différemment à la façon dont je conçois des applications et écris du code.
Il y a une partie du livre avec laquelle je ne suis pas d'accord, mais qui n'a pas de sens pour moi, en particulier en ce qui concerne les conventions de dénomination des interfaces. Voici le texte, tiré directement du livre. J'ai mis en évidence l'aspect de ce qui me rend confus et j'aimerais des éclaircissements.
Je préfère laisser les interfaces sans fioritures. Le je précédent, si courant dans les héritages actuels, constitue au mieux une distraction et trop d'informations au pire. Je ne veux pas que mes utilisateurs sachent que je leur donne une interface .
C'est peut-être parce que je ne suis qu'un étudiant, ou peut-être parce que je n'ai jamais fait de programmation professionnelle ou en équipe, mais je voudrais que l'utilisateur sache qu'il s'agit d'une interface. Il y a une grande différence entre implémenter une interface et étendre une classe.
Donc, ma question se résume à: "Pourquoi devrions-nous cacher le fait qu'une partie du code attend une interface?"
Modifier
En réponse à une réponse:
Si votre type est une interface ou une classe, c'est votre affaire, pas celle de quelqu'un qui utilise votre code. Donc, vous ne devriez pas divulguer les détails de votre code dans ce code tiers.
Pourquoi ne devrais-je pas "divulguer" les détails de savoir si un type donné est une interface ou une classe en code tiers? N’est-il pas important que le développeur tiers qui utilise mon code sache s’il va implémenter une interface ou étendre une classe? Est-ce que les différences ne sont tout simplement pas aussi importantes que je les fais paraître dans mon esprit?
la source
to know whether they will be implementing an interface or extending a class
: oui, mais la plupart des utilisateurs de votre code l'appelleront, ne l'implémenteront pas ou ne l'étendront pas, et ils ne se soucient vraiment pas de savoir lequel.Réponses:
Si vous vous arrêtez pour y penser, vous verrez qu'une interface n'est pas très différente sémantiquement d'une classe abstraite:
En fait, les distinctions les plus importantes entre les classes et les interfaces sont les suivantes:
Etant donné que les seules distinctions particulièrement significatives entre les classes et les interfaces tournent autour de (a) données privées et (b) hiérarchie de types - aucune d’entre elles ne faisant la moindre différence pour un appelant - il n’est généralement pas nécessaire de savoir si un type est une interface ou une classe. Vous n'avez certainement pas besoin de l' indication visuelle .
Cependant, il faut tenir compte de certains cas. En particulier, si vous utilisez la réflexion, l’interception, les proxy / mixins dynamiques, le tissage de bytecodes, la génération de code ou tout ce qui implique de manipuler directement le système de dactylographie de l’environnement ou le code lui-même - il est alors très utile et parfois nécessaire de le savoir directement. bat si vous avez affaire à une interface ou une classe. Vous ne voulez manifestement pas que votre code échoue mystérieusement parce que vous avez essayé d'ajouter une classe plutôt qu'un interface en tant que mixin.
Toutefois, dans le cas d’un code de logique métier standard et standard, il n’est pas nécessaire d’annoncer les distinctions entre les classes abstraites et les interfaces, car elles ne seront jamais prises en compte.
Ceci dit, j’ai quand même tendance à préfixer mes interfaces C #,
I
car c’est la convention .NET utilisée et préconisée par Microsoft. Et lorsque j'explique les conventions de codage à un nouveau développeur, il est beaucoup moins compliqué d'utiliser les règles de Microsoft que d'expliquer pourquoi nous avons nos propres règles "spéciales".la source
I
d'une interface peut donner un bon nom à une classe pour contenir des méthodes statiques associées à l'interface (par exempleEnumerable<T>.Empty
ouComparer<T>.Default
).class Foo
s'étend déjàclass Bar
, il n'est pas immédiatement évident de savoir s'ilclass Bar
est étendu ou implémenté. Alors maintenant, vous devez regarder dans l’en-class Bar
tête pour décider s’il est acceptable de modifierclass Foo
pour étendreclass Baz
. De plus, le préfixe I donne un indice visuel évident si la "classe de base unique" est violée ou non - vous obtenez ainsi un contrôle de cohérence à chaque fois que vous ouvrez un en-tête.À bien des égards, la cohérence est plus importante que la convention. Tant que vos schémas de nommage sont cohérents, ils ne seront pas difficiles à travailler. Préfixe les interfaces avec un I si vous préférez, ou laissez simplement le nom sans fioritures, cela ne m'importe pas tant que vous choisissez un style et vous y tenez!
la source
Le livre est plein de bonnes choses, mais j'ajouterais quand même "I" au nom de l'interface.
Imaginez que vous ayez
public interface ILog
une implémentation par défautpublic class Log
.Maintenant, si vous décidez de le faire
public interface Log
, vous devez tout à coup changer de classe de journal enpublic class LogDefault
ou quelque chose du genre. Vous êtes passé d’un personnage supplémentaire à sept, ce qui est une perte de temps.Il y a souvent une ligne de démarcation entre théorie et pratique. En théorie, c'est une très bonne idée, mais en pratique, ce n'est pas très bon.
la source
Log
-il? Se connecter à la console? Pour syslog? À nulle part? Ceux-ci devraient être appelésConsoleLog
,SyslogLog
etNullLog
, respectivement.Log
n'est pas un bon nom pour une classe concrète, car ce n'est pas un nom concret.MyClass
ait une variation mocassable , non? En d’autres termes, nous utilisons souvent des interfaces pour rendre les méthodes publiques réellement publiques d’une manière qui permette les tests. (Ne dites pas que c'est bon ou mauvais, mais comprendre que la motivation pour les interfaces est souvent simplement de faire en sorte que les classes qui sont des dépendances facilement moquables explique le mappageMyClass
de 1 à 1.IMyClass
)Il ne s’agit pas de mettre en oeuvre l’interface ou d’étendre une classe. Dans ces cas, vous savez quand même ce que vous faites.
Cependant, lorsqu'un code tiers (un autre module de l'application par exemple) manipule vos données, ce code ne doit pas vous inquiéter si vous présentez une interface ou une classe.
C'est tout le but de l'abstraction. Vous présentez à ce code tiers un objet d'un type donné. Ce type donné a une fonction membre que vous pouvez appeler. C'est assez.
Si votre type est une interface ou une classe, c'est votre affaire, pas celle de quelqu'un qui utilise votre code. Vous ne devriez donc pas divulguer les détails de votre code dans ce code tiers.
Par ailleurs, les interfaces et les classes sont des types de référence à la fin. Et c'est ce qui compte. C'est donc ce que votre convention de nommage doit souligner.
la source
La plupart des réponses semblent supposer que le langage de programmation est Java ou C # où il existe un concept (ou mot clé) pour les interfaces / classes abstraites. Dans ces cas, dans un IDE décent, il est immédiatement visible avec quel type de classe on traite et l'écriture
public interface IMyClass
est une duplication inutile. C'est comme si vous n'écriviez paspublic final FinalMyClass
- imaginez de mettre chaque mot clé dans le nom de la classe.Cependant, à mon avis, en C ++, la situation est un peu différente, car il faut plonger dans le fichier d’en-tête et voir si la classe dispose de méthodes virtuelles pour déterminer s’il s’agit d’une classe abstraite. Dans ce cas, je peux clairement voir un argument pour écrire
class AbstractMyClass
.Donc, ma réponse serait: Cela dépend du langage de programmation.
Mise à jour 2016: 2 ans d'expérience de C ++ plus tard, je considérerais maintenant probablement comme une mauvaise pratique de préfixer les noms de classe
Abstract
, bien que je dirais qu'il existe probablement des bases de code si complexes que cela pourrait avoir un sens.la source
Suivez les idiomes de la langue que vous utilisez.
Chaque langue a ses propres idiomes et directives de codage. Par exemple, en C #, il est idiomatique de préfixer les interfaces avec I. In Go, ce n’est pas le cas.
la source
Dans mon code (Java), j'ai tendance à avoir cette convention dans les API que j'expose aux appelants:
Par non fonctionnel, j'entends des choses comme des structures de données pures (telles que des classes qui agissent comme des ponts vers la sérialisation XML ou l'ORM), des énumérations et des exceptions, qui ne peuvent pas toutes être des interfaces (vous pouvez le faire pour les classes de données pures , mais c’est beaucoup de travail pour très peu d’avantages car il n’ya rien que ces classes fassent sauf des données).
En termes de conventions de dénomination, les interfaces ont tendance à mapper sur des noms d'acteur (par exemple
FooBarWatcher
) ou des adjectifs (par exempleFooBarWatchable
), et les classes de données pures et les énumérations sont mappées sur des noms non actifs (par exempleFooBarStatus
); l'EDI peut guider le consommateur d'API sans conventions de dénomination particulières. Les exceptions suivent les conventions habituelles de Java (FooBarException
,FooBarError
,FooBarFault
) bien sûr.Je vais aussi souvent mettre les interfaces dans leur propre paquet ou même dans leur propre bibliothèque, juste pour m'assurer que je ne suis pas tenté de casser mes propres règles. (Cela me permet également de gérer la construction lorsque je dérive le code à partir de sources externes telles que des documents WSDL.)
la source
I
préfixes sur les interfaces. Bien sûr que c'est une interface! C'est dans une API (ou SPI)!