Existe-t-il une logique d'utilisation différente pour les classes / interfaces abstraites en C ++ et Java

13

Selon Herb Sutter, on devrait préférer les interfaces abstraites (toutes les fonctions virtuelles pures) aux classes abstraites en C ++ pour découpler l'implémentation autant que possible. Bien que je trouve personnellement cette règle très utile, j'ai récemment rejoint une équipe avec de nombreux programmeurs Java et dans le code Java, cette directive ne semble pas exister. Les fonctions et leurs implémentations sont très fréquemment situées dans des classes abstraites. Donc, j'ai tout faux Herb Sutter même pour C ++ ou y a-t-il une différence générale dans l'utilisation des fonctions abstraites en C ++ par rapport à Java. Les classes abstraites avec du code d'implémentation sont-elles plus sensées en Java qu'en C ++ et si oui pourquoi?

Martin
la source
1
J'ai eu quelques doutes et je l'ai finalement mis ici car cela pourrait être dû à certains principes de conception qui me manquent à propos du java oo. Il ne s'agit donc pas d'un conseil général mais plutôt du bien et du mal d'utiliser la langue
Martin
Les interfaces sont censées être purement virtuelles. L'idée des classes abstraites est qu'elles sont partiellement implémentées, et c'est à l'implémentation de combler les lacunes sans répéter inutilement le code (par exemple, pourquoi avoir write (byte) et write (int) dans chaque sous-classe quand vous pouvez avoir le classe abstraite call write (byte) from write (int))
1
Peut-être lié: stackoverflow.com/q/1231985/484230 donne une raison pour préférer les classes abstraites en java. Pour C ++, cette raison ne semble pas valable en raison de l'existence de fonctions gratuites pouvant ajouter des fonctionnalités au niveau de l'interface
Martin
1
Je pense que la règle d'or est de "rendre les classes non-feuilles abstraites", mais cela ne fait aucune exigence "uniquement pure" ou "vide".
Kerrek SB
1
Si cela fonctionne pour vous, cela fonctionne pour vous. Je ne vois vraiment pas pourquoi les gens paniquent une fois que leur code n'adhère plus aux dernières opinions.
James

Réponses:

5

La POO a une composition et une substitution.

C ++ possède plusieurs héritages, une spécialisation de modèle, une intégration et une sémantique de valeur / déplacement / pointeur.

Java possède un héritage et des interfaces uniques, une sémantique d'intégration et de référence.

L'école OOP utilise couramment ces langages pour utiliser l'héritage pour la substitution d'objets et l'incorporation pour la composition. Mais vous avez également besoin d'un ancêtre commun et d'un moyen de lancer le runtime (en C ++ est appelé dynamic_cast, en Java, il suffit de demander une interface à un autre).

Java fait tout cela par sa propre java.lang.Objecthiérarchie enracinée. C ++ n'a pas de racine commune prédéfinie, vous devriez donc au moins la définir, pour arriver à une même "image" (mais cela limite certaines possibilités C ++ ...).

Après cela, la possibilité d'avoir un polymorphisme au moment de la compilation (pensez au CRTP) et une sémantique de valeur peuvent également offrir d'autres alternatives à la façon dont le concept d '"objet OOP" peut être porté dans un programme C ++.

Vous pouvez même imaginer l'hérésie d'utiliser l'incorporation et la conversion implicite pour gérer la substitution et l'héritage privé pour gérer la composition, en inversant en fait le paradigme scolaire traditionnel. (Bien sûr, cette façon est 20 ans plus jeune que l'autre, alors ne vous attendez pas à un large soutien communautaire pour le faire)

Ou vous pouvez imaginer une base commune virtuelle à toutes les classes, une interface de formulaire (pas d'implémentation) aux classes finales (entièrement implémentées) passant par des interfaces partiellement implémentées et des clusters d'interface pairs, en utilisant la "dominance" comme répartition de l'interface vers les implémentations via un "multi-stacked" -parallelogram "schéma d'héritage.

Comparer OOP à java à C ++ en supposant qu'il n'y a qu'un seul et unique moyen OOP limite les capacités des deux langages.

Forcer C ++ à adhérer strictement aux idiomes de codage Java dénature C ++ car forcer Java à se comporter comme un langage de type C ++ dénature Java.

Ce n'est pas une question de "sensibilité" mais de "mécanismes d'agrégation" différents que les deux langues ont et une manière différente de les combiner qui rend un idiome plus rentable dans une langue que dans l'autre et vice versa.

Emilio Garavaglia
la source
1
Je pense que cette réponse est très intéressante, car elle décrit succinctement les fonctionnalités du langage comme un outil pour oo et les principes de conception uniquement comme une aide et non comme une doctrine. Cependant, vous n'avez pas besoin d'une racine commune si vous voulez faire oo en c ++. C'est tout simplement faux, également pour le fait que vous avez des opérateurs et des modèles (qui sont une alternative très puissante à l'arborescence principale de java comme vous l'avez également souligné). En dehors de cela, vos points sont les plus utiles dans toutes les réponses
Martin
1
@Martin: Dans le "sens technique", vous avez raison, mais si vous avez besoin d'un polymorophisme à l'exécution (car le type réel des objets instanciés dépend de l'entrée du programme) une "racine" ("a" est un article, pas un raccourci pour " un seul et unique ") est ce qui rend tous les" cousins ​​"objets, et la hiérarchie accessible à l'exécution. Différentes racines sont à l'origine d' ascendances différentes qui ne sont pas liées entre elles. Que ce soit «bon» ou «mauvais» est une question de contexte, pas d'idiome.
Emilio Garavaglia
C'est vrai. Je pensais que vous faisiez référence à la création artificielle d'une racine générale pour un programme c ++ entier et je l'ai vu comme un défaut qu'il n'est pas présent, par rapport à Java. Mais après votre montage, vous faites le point très clairement. Merci encore
Martin
12

Le principe est valable pour les deux langues, mais vous ne faites pas une comparaison équitable. Vous devez comparer les classes abstraites pures C ++ avec les interfaces Java.

Même en C ++, vous pouvez avoir des classes abstraites qui ont certaines des fonctions implémentées, mais dérivent d'une classe abstraite pure (pas d'implémentations). En Java, vous auriez les mêmes classes abstraites (avec certaines implémentations), qui peuvent dériver des interfaces (pas d'implémentations).

Luchian Grigore
la source
Alors, quand préférez-vous une classe abstraite à une classe d'interface en c ++. J'ai toujours opté pour l'interface et les fonctions non membres en c ++.
Martin
1
@Martin qui dépend de la conception. Fondamentalement, préférez toujours une interface. Mais les règles " toujours " ont des exceptions ...
Luchian Grigore
C'est vrai, mais dans le code Java, je vois des classes abstraites représentant largement la majorité. Cela pourrait-il être dû au fait que les fonctions libres travaillant sur les interfaces ne sont pas possibles en java?
Martin
3
Les fonctions libres de @Martin ne sont pas du tout possibles en Java, donc cela pourrait être une raison, oui. Bon endroit! Répondu à votre propre question! Vous pouvez ajouter une réponse vous-même, je pense que c'est tout.
Luchian Grigore
4

Généralement, les mêmes principes OO sont valables pour Java et C ++. Cependant, une grande différence est que C ++ prend en charge l'héritage multiple tandis qu'en Java, vous ne pouvez hériter que d'une seule classe. C'est la raison principale pour laquelle Java a des interfaces, je crois, pour compléter le manque d'héritage multiple et probablement pour restreindre ce que vous pouvez en faire (car il y a beaucoup de critiques sur l'abus d'héritage multiple). Donc, probablement dans l'esprit des programmeurs Java, il y a une distinction plus forte entre les classes abstraites et les interfaces. Les classes abstraites sont utilisées pour partager et hériter du comportement tandis que les interfaces sont simplement utilisées pour ajouter des fonctionnalités supplémentaires. N'oubliez pas qu'en Java, vous ne pouvez hériter que d'une seule classe, mais vous pouvez avoir plusieurs interfaces. En C ++ cependant, les classes abstraites pures (c'est-à-dire une "interface C ++") sont utilisé pour partager et hériter d'un comportement contrairement à l'objectif d'une interface Java (même si vous devez toujours implémenter les fonctions), par conséquent, l'utilisation est différente des interfaces Java.

Jesse Good
la source
0

Parfois, il est logique d'avoir une implémentation par défaut. Par exemple, une méthode générique PrintError (string msg) qui s'applique à toutes les sous-classes.

virtual PrintError(string msg) { cout << msg; }

Il peut toujours être remplacé si cela est vraiment nécessaire, mais vous pouvez éviter au client quelques tracas en lui permettant d'appeler simplement la version générique.

Science fiction
la source