J'avais l'habitude de créer beaucoup de classes / méthodes abstraites. Puis j'ai commencé à utiliser des interfaces.
Maintenant, je ne sais pas si les interfaces ne rendent pas les classes abstraites obsolètes.
Vous avez besoin d'un cours totalement abstrait? Créez plutôt une interface. Vous avez besoin d'une classe abstraite avec une implémentation? Créez une interface, créez une classe. Hériter de la classe, implémenter l'interface. Un avantage supplémentaire est que certaines classes peuvent ne pas avoir besoin de la classe parente, mais simplement implémenter l'interface.
Alors, les classes / méthodes abstraites sont-elles obsolètes?
object-oriented
architecture
inheritance
interfaces
Boris Yankov
la source
la source
Réponses:
Non.
Les interfaces ne peuvent pas fournir une implémentation par défaut, les classes abstraites et la méthode peuvent. Ceci est particulièrement utile pour éviter la duplication de code dans de nombreux cas.
C'est également un très bon moyen de réduire le couplage séquentiel. Sans méthodes / classes abstraites, vous ne pouvez pas implémenter de modèle de méthode. Je vous suggère de consulter cet article de Wikipédia: http://fr.wikipedia.org/wiki/Template_method_pattern
la source
Une méthode abstraite existe afin que vous puissiez l'appeler à partir de votre classe de base mais l'implémenter dans une classe dérivée. Donc, votre classe de base sait:
C'est un moyen relativement agréable d'implémenter un trou dans le motif du milieu sans que la classe dérivée connaisse les activités d'installation et de nettoyage. Sans méthodes abstraites, vous auriez à compter sur la classe dérivée pour implémenter
DoTask
et se rappeler d’appelerbase.DoSetup()
etbase.DoCleanup()
tout le temps.modifier
Merci également à deadalnix d’avoir posté un lien vers le modèle de méthode , ce que j’ai décrit ci-dessus sans connaître le nom. :)
la source
public void DoTask(Action doWork)
Fruit
et que votre classe dérivée estApple
, vous souhaitez appelermyApple.Eat()
, pasmyApple.Eat((a) => howToEatApple(a))
. En outre, vous ne voulezApple
pas avoir à appelerbase.Eat(() => this.howToEatMe())
. Je pense qu'il est plus simple de remplacer une méthode abstraite.Non, ils ne sont pas obsolètes.
En fait, il existe une différence obscure mais fondamentale entre les classes / méthodes abstraites et les interfaces.
si l'ensemble des classes dans lesquelles l'une d'elles doit être utilisée a un comportement commun qu'elles partagent (classes apparentées, je veux dire), optez pour Classes / méthodes abstraites.
Exemple: commis, agent, directeur - toutes ces classes ont en commun CalculateSalary (), utilisent des classes de base abstraites.CalculerSalary () peut être implémenté différemment, mais il existe certaines autres choses comme GetAttendance (), par exemple, qui a une définition commune dans la classe de base.
Si vos classes n'ont rien de commun (classes sans rapport, dans le contexte choisi) entre elles mais a une action qui est très différente dans la mise en œuvre, optez pour Interface.
Exemple: vache, banc, voiture, classes apparentées au télésope, mais Isortable peut être là pour les trier dans un tableau.
Cette différence est généralement ignorée lorsqu’elle est abordée dans une perspective polymorphe. Mais personnellement, j’ai le sentiment qu’il ya des situations où l’un est plus apte que l’autre pour la raison exposée ci-dessus.
la source
Outre les autres bonnes réponses, il existe une différence fondamentale entre les interfaces et les classes abstraites que personne n'a mentionnée spécifiquement, à savoir que les interfaces sont beaucoup moins fiables et imposent donc une charge de test beaucoup plus lourde que les classes abstraites. Par exemple, considérons ce code C #:
Je suis assuré qu'il n'y a que deux chemins de code que je dois tester. L’auteur de Frobbit peut s’appuyer sur le fait que le frobber est rouge ou vert.
Si à la place nous disons:
Je ne sais absolument plus rien des effets de cet appel à Frob. Je dois m'assurer que tout le code de Frobbit est robuste contre toute implémentation possible d'IFrobber , même celle d'implémentations par des personnes incompétentes (mauvaises) ou activement hostiles envers moi ou mes utilisateurs (bien pire).
Les classes abstraites vous permettent d'éviter tous ces problèmes; Utilise les!
la source
Vous le dites vous-même:
cela semble beaucoup de travail comparé à «hériter de la classe abstraite». Vous pouvez travailler pour vous-même en abordant le code dans une perspective puriste, mais je trouve que j’en ai déjà assez à faire sans essayer d’ajouter à ma charge de travail sans aucun avantage pratique.
la source
Comme je l'ai commenté sur @deadnix post: Les implémentations partielles sont un anti-modèle, malgré le fait que le modèle de modèle les formalise.
Une solution propre à cet exemple wikipedia du modèle de modèle :
Cette solution est préférable car elle utilise la composition au lieu de l'héritage . Le modèle de modèle introduit une dépendance entre l'implémentation des règles de monopole et l'implémentation de la manière dont les jeux doivent être exécutés. Cependant, ce sont deux responsabilités totalement différentes et il n’ya aucune bonne raison de les associer.
la source
Même votre alternative proposée inclut l’utilisation de classes abstraites. De plus, comme vous n'avez pas spécifié le langage, je vais aller de l'avant et dire que le code générique est de toute façon la meilleure option que l'héritage fragile. Les classes abstraites présentent des avantages importants par rapport aux interfaces.
la source
Les classes abstraites ne sont pas des interfaces. Ce sont des classes qui ne peuvent pas être instanciées.
Mais alors vous auriez une classe inutile non abstraite. Des méthodes abstraites sont nécessaires pour combler les lacunes dans les fonctionnalités de la classe de base.
Par exemple, étant donné cette classe
Comment implémenteriez-vous cette fonctionnalité de base dans une classe qui n'a ni
Frob()
niIsFrobbingNeeded
?la source
Je suis un créateur de framework de servlet où les classes abstraites jouent un rôle essentiel. Je dirais plus, j'ai besoin de méthodes semi-abstraites, quand une méthode doit être remplacée dans 50% des cas et j'aimerais que le compilateur avertisse que cette méthode n'a pas été remplacée. Je résous le problème en ajoutant des annotations. Pour revenir à votre question, il existe deux cas d'utilisation différents de classes abstraites et d'interfaces, et personne n'est jusqu'à présent obsolète.
la source
Je ne pense pas qu'elles soient obsolètes par les interfaces, mais elles pourraient l'être aussi par le modèle de stratégie.
L'utilisation principale d'une classe abstraite consiste à reporter une partie de l'implémentation; pour dire, "cette partie de l'implémentation de la classe peut être différente".
Malheureusement, une classe abstraite oblige le client à le faire via l' héritage . Alors que le modèle de stratégie vous permettra d’obtenir le même résultat sans héritage. Le client peut créer des instances de votre classe plutôt que de toujours définir les leurs, et son "implémentation" (comportement) peut varier de manière dynamique. Le modèle de stratégie présente l'avantage supplémentaire de pouvoir modifier le comportement au moment de l'exécution, pas seulement au moment de la conception, et offre également un couplage nettement plus faible entre les types impliqués.
la source
J'ai généralement rencontré beaucoup, beaucoup plus de problèmes de maintenance associés à des interfaces pures que des ABC, même des ABC utilisés avec un héritage multiple. YMMV - dunno, peut-être que notre équipe les a juste mal utilisés.
Cela dit, si nous utilisons une analogie du monde réel, dans quelle mesure y a-t-il des interfaces pures complètement dépourvues de fonctionnalité et d’état? Si j'utilise l'USB comme exemple, c'est une interface raisonnablement stable (je pense que nous sommes maintenant à l'USB 3.2, mais elle a également maintenu une compatibilité ascendante).
Pourtant, ce n'est pas une interface sans état. Ce n'est pas dépourvu de fonctionnalité. Cela ressemble plus à une classe de base abstraite qu'à une interface pure. C'est en fait plus proche d'une classe concrète avec des exigences fonctionnelles et d'état très spécifiques, la seule abstraction étant ce qui se branche sur le port étant la seule partie substituable.
Sinon, il s'agirait simplement d'un "trou" dans votre ordinateur avec un facteur de forme normalisé et des exigences fonctionnelles beaucoup plus souples qui ne feraient rien par lui-même jusqu'à ce que chaque fabricant propose son propre matériel pour faire en sorte que ce trou fasse quelque chose, puis cela devient un standard beaucoup plus faible et rien de plus qu'un "trou" et une spécification de ce qu'il doit faire, mais pas de disposition centrale sur la façon de le faire. En attendant, nous pourrions nous retrouver avec 200 façons différentes de le faire après que tous les fabricants de matériel essaient de trouver leurs propres moyens pour associer fonctionnalité et état à ce "trou".
Et à ce stade, certains fabricants pourraient présenter des problèmes différents de ceux des autres. Si nous devons mettre à jour la spécification, nous pourrions avoir 200 implémentations concrètes de ports USB avec des manières totalement différentes d'aborder la spécification, de la mettre à jour et de la tester. Certains fabricants peuvent développer de facto des implémentations standard qu'ils partagent entre eux (votre classe de base analogique implémentant cette interface), mais pas tous. Certaines versions peuvent être plus lentes que d'autres. Certains pourraient avoir un meilleur débit mais une latence plus faible ou vice versa. Certains pourraient utiliser plus de batterie que d'autres. Certains risquent de s'effondrer et de ne pas fonctionner avec tout le matériel censé fonctionner avec des ports USB. Certains pourraient nécessiter la mise en place d’un réacteur nucléaire, ce qui a tendance à donner à ses utilisateurs un empoisonnement par rayonnement.
Et c'est ce que j'ai trouvé, personnellement, avec des interfaces pures. Dans certains cas, ils peuvent avoir un sens, par exemple simplement pour modéliser le facteur de forme d’une carte mère par rapport à un boîtier de processeur. Les analogies des facteurs de forme sont en effet à peu près sans état et dépourvues de fonctionnalité, comme dans le cas du "trou" analogique. Mais j’estime souvent que c’est une grave erreur pour les équipes de considérer cela comme étant supérieur dans tous les cas, même pas proche.
Au contraire, je pense que beaucoup plus de cas seraient résolus par ABC que par les interfaces si ce sont les deux choix possibles, à moins que votre équipe soit si gigantesque qu’il soit souhaitable de disposer de l’analogique supérieure à 200 implémentations USB concurrentes plutôt qu’une norme centrale. maintenir. Dans une ancienne équipe dans laquelle je faisais partie, je devais en réalité me battre pour assouplir la norme de codage afin de permettre les ABC et l'héritage multiple, principalement en réponse aux problèmes de maintenance décrits ci-dessus.
la source