Chaque classe que j'écris doit-elle adhérer à une interface?


10

J'écris un jeu en Typescript, et j'ai décidé d'aller dans le sens où j'allais essayer d'adhérer à l'idée de " programmation basée sur l'interface ", où vous écrivez du code basé sur une interface, au lieu de l'implémentation, d'un objet.

J'ai écrit un bon nombre d'interfaces et de classes qui les implémentent, puis j'ai pris du recul et j'ai réalisé que les classes étaient assez simples pour que je n'aie probablement jamais besoin de changer l'implémentation, car il n'y a vraiment qu'une seule façon de faire ce que le la classe fait (déplacer un Phaser.Sprited'une manière restreinte pour agir comme un tank).

Ensuite, je me souviens avoir lu il y a quelques années sur l'idée de YAGNI , qui est fondamentalement que vous ne devriez pas sur-concevoir votre code pour inclure des choses que vous n'utiliserez peut-être jamais.

En suivant les meilleures pratiques, chaque classe devrait-elle implémenter une interface, ou devriez-vous la limiter aux classes que vous prévoyez d'être potentiellement remplacées à l'avenir?


N'oubliez pas que lorsque vous utilisez une classe réelle comme paramètre, vous codez également vers une interface, vers l'interface de cette classe. Personne ne vous oblige à utiliser cette classe spécifique, vous pourriez tout aussi bien en hériter et fournir des fonctionnalités complètement nouvelles, mais cela ne semble pas correct. Des interfaces existent pour faciliter la compréhension des gens autour de l'idée. Cela étant triste, non, vous n'avez pas vraiment besoin d'interfaces pour tout.
Andy

Réponses:


6

La raison d'avoir des interfaces est parce qu'elle simplifie le polymorphisme. Cela signifie que vous pouvez envoyer des instances par contrat plutôt que de connaître leur implémentation réelle. Par exemple, vous pouvez envoyer un "Reader" à une méthode, afin qu'une telle méthode appelée puisse utiliser sa méthode "read ()". En déclarant une interface "Reader", vous pouvez rendre tout objet conforme à ce contrat, en mettant en œuvre les méthodes qu'il précise. De cette façon, tout appelant peut supposer que certaines méthodes existeront, même si les objets sous-jacents au contrat peuvent être complètement différents.

Cela dissocie le polymorphisme d'une classe de base, et permet également entre les frontières de la chaîne de classe, en impliquant un contrat.

Maintenant ... Ceci n'est utile que si vous avez besoin de plusieurs chaînes d'héritage pour agir de la même manière, ou si vous aurez de nombreuses classes de base avec un comportement commun que vous voudrez transmettre à d'autres utilisateurs.

Si vous n'avez qu'une seule chaîne d'héritage (baseclass-> subclass) ou juste la base, ou si vous n'avez pas vraiment besoin de PASSER les objets associés à quelqu'un d'autre ou de les utiliser de manière générique, alors vous n'ajouterez pas d'implémentations d'interface, car cette notion est alors inutile.


J'ajouterais également que les interfaces devraient modéliser les abstractions, alors ne les créez pas simplement en hissant tous les membres publics d'une classe. Pensez à ce que cette classe représente et ne mettez que ces choses dans l'interface que tout objet de ce type devrait avoir. Vous pouvez également finir par créer plusieurs interfaces (par exemple Moveset Shootspour votre exemple de réservoir) pour une seule classe.
TMN

7

Si vous n'êtes pas sûr d'avoir réellement besoin des interfaces, vous n'en avez probablement pas. C'est le cœur du principe YAGNI. Lorsque vous devez pouvoir échanger l'implémentation, vous introduisez une interface.


6

Créer une interface pour chaque classe est un peu exagéré. D'un point de vue purement orienté objet, chaque classe possède déjà une interface . Une interface n'est rien de plus que les méthodes accessibles au public et les membres de données d'une classe.

Nous utilisons généralement "interface" pour signifier "une classe Java définie à l'aide du interfacemot - clé" ou "une classe C ++ avec uniquement des fonctions virtuelles pures publiques". Ce sont des abstractions utiles, car elles dissocient l'interface d'une classe de son implémentation.

Dans cet esprit, utilisez des interfaces lorsque cela est approprié.

  • Y aura-t-il plusieurs implémentations pour une interface? Si tel est le cas, cette situation peut être un candidat pour extraire des méthodes communes accessibles au public dans une interface.

  • Vais-je transférer des implémentations d'une interface potentielle à d'autres modules qui ne devraient pas connaître le fonctionnement interne de mon module? Une interface peut être utile ici, car elle réduit la taille du point de contact entre les modules.

Remarquez les mots de la belette ci-dessus: "peut" être un candidat, "peut être" utile. Il n'y a pas de règle stricte et rapide qui dit «oui» ou «non» pour une situation donnée.

Cela étant dit, avoir une interface pour tout est probablement exagéré. Si vous voyez ce schéma, vous allez loin:

interface I {
  int getA()
  int getB()
  ...
  int getY()
  int getZ()
}

class C : I {
  int getA() { ... }
  int getB() { ... }
  ...
  int getY() { ... }
  int getZ() { ... }
}

Une autre raison des interfaces est que si votre système de test le rend nécessaire, cela peut dépendre de votre langage et de votre framework de test. J'espère que vous n'avez pas besoin de faire cela, bien sûr.
Darien

@ Darien: La raison pour laquelle un système de test peut nécessiter des interfaces est que le test utilise en fait une implémentation différente (simulée), vous ramenant à la première puce quand une interface est appropriée.
Bart van Ingen Schenau

Quelques trucs géniaux ici. Réfléchissons juste à ceci: Une interface n'est rien de plus que les méthodes accessibles au public et les membres de données d'une classe . Bien que cela soit vrai pour une implémentation d' interface , cela ne vaut certainement pas pour une interface qui n'est jamais implémentée, bien qu'accordée - ce serait quelque chose d'une odeur de code.
Robbie Dee

@RobbieDee c'est vrai par exemple pour un Java interface, il se trouve que tout dans le Java interfaceest aussi son interface publique (concept OO).

Toutes les personnes! Notez les 2e et 3e phrases de la réponse. Plastifiez-le. Mettez-le dans votre portefeuille. Référence souvent.
radarbob

5

Il peut être très tentant pour les nouveaux développeurs de considérer les interfaces comme un inconvénient que vous ajoutez simplement parce qu'on vous dit que c'est une "meilleure pratique". Si vous êtes en train de faire aveuglément cela, il sent la programmation culte du cargo .

Il existe un certain nombre de très bonnes raisons pour lesquelles vous devriez envisager des interfaces pour des développements plus importants plutôt que de regrouper un ensemble de classes.

Cadres moqueurs

Si vous souhaitez tester une application multicouche sans avoir à créer des objets pour les différentes couches, vous voudrez sans aucun doute utiliser des cadres de simulation pour simuler les couches. Les interfaces sont largement utilisées ici.

Injection de dépendance

Bien qu'ils soient un peu tombés en disgrâce en raison du ballonnement qu'ils ajoutent, l'idée est saine - la possibilité d'échanger simplement des concrétions basées sur une interface. Le principe peut également être trouvé dans de nombreux modèles de conception tels que le modèle d'usine.


Ces approches sont résumées dans le D à partir des principes SOLID . Le hic, c'est - utilisez des abstractions, pas des concrétions.

Comme les modèles de conception eux-mêmes, quand les utiliser est un jugement. Le point à retenir est de comprendre à quel point les interfaces sont utiles et pas seulement de les coller sur vos cours parce que vous vous sentez obligé. Il y a une bonne discussion des divers mérites de DIP et YAGNI ici .


Notez que l'injection de dépendances et les conteneurs IoC sont deux choses complètement différentes (l'une est un principe de conception et l'autre est une façon de mettre concrètement ce principe en pratique en utilisant divers cadres). Vous pouvez utiliser DI sans utiliser de conteneur IoC.
sara

@kai "Vous pouvez utiliser DI sans utiliser de conteneur IoC" . Je pense que les magasins de développement plus matures peuvent (et font). Il n'est tout simplement pas nécessaire de polluer le code pour ce qui est apparemment un concept simple. Joel est du même avis.
Robbie Dee

YAGNI est très délicat. Quelle que soit la philosophie, dans la plupart des cas, en réalité, YAGNI est utilisé comme excuse pour couper les coins ronds. Éviter le couplage doit être pris plus au sérieux. Il est frustrant de voir dans de nombreuses réunions de mêlée, un développeur se dit bloqué parce que quelqu'un d'autre n'a pas terminé son travail. Comment se fait-il que le fait de bloquer un autre développeur fait gagner du temps à quelqu'un? Ma suggestion est d'utiliser une interface pour éviter le couplage. Bien sûr, il n'est pas nécessaire d'utiliser une interface pour les classes Model.
Ripal Barot du
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.