Classe abstraite et interface de java
Fermé
wiam_
Messages postés
168
Date d'inscription
samedi 1 mars 2008
Statut
Membre
Dernière intervention
11 août 2013
-
24 déc. 2008 à 23:12
ines2013 - 5 avril 2013 à 10:51
ines2013 - 5 avril 2013 à 10:51
A voir également:
- Difference classe abstraite interface
- Difference tcp udp - Guide
- Difference million milliard - Accueil - Technologies
- Difference actif et en ligne messenger - Forum Facebook Messenger
- Différence ddr3 et ddr3l ✓ - Forum Matériel & Système
- Common interface 5v only - Forum TNT / Satellite / Réception
8 réponses
Bonjour messieur,
je sais que pour certain la "factorisation" semble inutile, mais ici, je vais vous expliquer ce à quoi ça sert. Il y a avant tout une grande nuance entre l'interface et la classe abstraite. L'interface permet de s'assurer que certaine classes filles auront sans aucun doute les méthodes définit dans celle-ci. La classes abstraite elle est un hybride entre une classe conventionnel et un interface. Elle offre une implémentation générique aux classes filles et également leur demande d'implémenter certaines choses pour lesquels la classe abstraite ne peut pas décider. Voici un exemple : Je veux réinventer Java2D et dessiner des formes a ma manière de A à Z. J'aurais donc une classe abstraite AbstraiteForme qui défini les méthodes de base telle que "setShader()", "setPosition()", etc. Par contre, la méthode dessiner() ne pourra pas être fournit par la classe abstraite. Donc cercle et carré auront a redéfinir comment elle voudront se déssiner. Les classes abstraite évite de réécrire 100 fois le meme bout de code dans chaque forme. Aussi, tu pourrais avoir une classe qui s'occupe de faire le draw. Avec une interface (ou une classe abstraite) ta pas besoin de savoir si tu va faire "dessiner()" sur un cercle ou un carré, tu sais juste que c'est une AbstraiteForme ou FormeIF et que ca ce dessine... Tu peux donc facilement ajouter ou retirer des forme dans ton système! :D Alors... la "factorisation"... c'est hyper puissant et ca sauve bcp de sous en dev (surtout coté maintenance!)
J'espere que c'est assez clair ;)
je sais que pour certain la "factorisation" semble inutile, mais ici, je vais vous expliquer ce à quoi ça sert. Il y a avant tout une grande nuance entre l'interface et la classe abstraite. L'interface permet de s'assurer que certaine classes filles auront sans aucun doute les méthodes définit dans celle-ci. La classes abstraite elle est un hybride entre une classe conventionnel et un interface. Elle offre une implémentation générique aux classes filles et également leur demande d'implémenter certaines choses pour lesquels la classe abstraite ne peut pas décider. Voici un exemple : Je veux réinventer Java2D et dessiner des formes a ma manière de A à Z. J'aurais donc une classe abstraite AbstraiteForme qui défini les méthodes de base telle que "setShader()", "setPosition()", etc. Par contre, la méthode dessiner() ne pourra pas être fournit par la classe abstraite. Donc cercle et carré auront a redéfinir comment elle voudront se déssiner. Les classes abstraite évite de réécrire 100 fois le meme bout de code dans chaque forme. Aussi, tu pourrais avoir une classe qui s'occupe de faire le draw. Avec une interface (ou une classe abstraite) ta pas besoin de savoir si tu va faire "dessiner()" sur un cercle ou un carré, tu sais juste que c'est une AbstraiteForme ou FormeIF et que ca ce dessine... Tu peux donc facilement ajouter ou retirer des forme dans ton système! :D Alors... la "factorisation"... c'est hyper puissant et ca sauve bcp de sous en dev (surtout coté maintenance!)
J'espere que c'est assez clair ;)
voilà ce n'est pas aussi compliquer :
une classe abstraite est une classe dont les méthode sont abstrait c.à.d lorsque tu déclare une méthode abstract automatiquement tu doit définir la classe " abstract" et elle est utiliser généralement quand d'autre classes vont hériter de cette classe et ils ont la même méthode que la classe mére donc ont l'écrit pour ne pas répeter les mêmes méthodes chaque fois dans ces classes.
pour les interfaces sont utilisés quand d'autres classes qui vont avoir les même méthodes ont les définies dans une interfaces et puis l'implémenter ultérieurement même si tu n'a pas besoin d'une méthode dans l'interface tu doit l'implémenter par exemple tu peut simplement écrire le nom de la méthode puis ouvrir et fermer l'accolade(je crois que s'écrit comme ça non accolade) comme ça : public void méthode(){}. Engénéral il n'y a pas une grande différence entre interface et classe abstraire ; une interface est un cas paticulier d'une classe abstarite .
bonne travail
une classe abstraite est une classe dont les méthode sont abstrait c.à.d lorsque tu déclare une méthode abstract automatiquement tu doit définir la classe " abstract" et elle est utiliser généralement quand d'autre classes vont hériter de cette classe et ils ont la même méthode que la classe mére donc ont l'écrit pour ne pas répeter les mêmes méthodes chaque fois dans ces classes.
pour les interfaces sont utilisés quand d'autres classes qui vont avoir les même méthodes ont les définies dans une interfaces et puis l'implémenter ultérieurement même si tu n'a pas besoin d'une méthode dans l'interface tu doit l'implémenter par exemple tu peut simplement écrire le nom de la méthode puis ouvrir et fermer l'accolade(je crois que s'écrit comme ça non accolade) comme ça : public void méthode(){}. Engénéral il n'y a pas une grande différence entre interface et classe abstraire ; une interface est un cas paticulier d'une classe abstarite .
bonne travail
Marco la baraque
Messages postés
996
Date d'inscription
vendredi 9 mai 2008
Statut
Contributeur
Dernière intervention
5 novembre 2009
329
25 déc. 2008 à 22:48
25 déc. 2008 à 22:48
Bonsoir,
Pour compléter ce que dit Walkhess, l'utilisation d'interfaces et de classes abstraites, ça a deux avantages.
- la factorisation de code. Selon certains, la factorisation est l'un des avantages de la programmation orientée objet, selon d'autres (et j'adhère à ce mouvement de pensée), la factorisation ça ne sert à rien, sauf parfois à concevoir de mauvais programme par souci de "sur-factorisation".
La factorisation, ça consiste à écrire des méthodes dans les classes, de manière à pouvoir les réutiliser sans les retaper dans les classes filles. C'est ce qui est utilisé lorsque tu implémentes des méthodes dans ta classe abstraite : si toutes les classes filles ont le même comportement pour cette méthode, alors elle doit être implémentée une et une seule fois dans la classe abstraite
- l'abstraction (le polymorphisme). C'est la manière d'utiliser une référence vers une interface (qui définit une série de méthodes), et d'utiliser une implémentation concrète, mais sans la connaître.
Par exemple, quand tu vas utiliser une liste (interface java.util.List), tu ne vas pas connaître quelle implémentation tu vas utiliser. Tu vas par contre savoir que tu peux insérer un élément à un endroit, en supprimer un, connaître la taille...
Et donc dans ton code, tu vas utiliser une référence vers une List.
Ensuite, tu vas utiliser réellement une implémentation de cette interface, mais d'un point de vue utilisation, tu ne sauras pas ce que c'est. Que ce soit une LinkedList (suppression et insertion en O(n) par exemple), ou une ArrayList(en temps constant pour ces méthodes), ça ne t'intéresse que lorsque tu compares les performances lors de l'implémentation initiale de ton ensemble. Une fois que c'est fait, tu fais abstraction de cet aspect "bas-niveau" pour te concentrer uniquement sur l'utilisation de ta liste, et donc tu passes par ton interface.
En espérant avoir été un peu plus loin, sans avoir été trop confus...
Cordialement
Pour compléter ce que dit Walkhess, l'utilisation d'interfaces et de classes abstraites, ça a deux avantages.
- la factorisation de code. Selon certains, la factorisation est l'un des avantages de la programmation orientée objet, selon d'autres (et j'adhère à ce mouvement de pensée), la factorisation ça ne sert à rien, sauf parfois à concevoir de mauvais programme par souci de "sur-factorisation".
La factorisation, ça consiste à écrire des méthodes dans les classes, de manière à pouvoir les réutiliser sans les retaper dans les classes filles. C'est ce qui est utilisé lorsque tu implémentes des méthodes dans ta classe abstraite : si toutes les classes filles ont le même comportement pour cette méthode, alors elle doit être implémentée une et une seule fois dans la classe abstraite
- l'abstraction (le polymorphisme). C'est la manière d'utiliser une référence vers une interface (qui définit une série de méthodes), et d'utiliser une implémentation concrète, mais sans la connaître.
Par exemple, quand tu vas utiliser une liste (interface java.util.List), tu ne vas pas connaître quelle implémentation tu vas utiliser. Tu vas par contre savoir que tu peux insérer un élément à un endroit, en supprimer un, connaître la taille...
Et donc dans ton code, tu vas utiliser une référence vers une List.
Ensuite, tu vas utiliser réellement une implémentation de cette interface, mais d'un point de vue utilisation, tu ne sauras pas ce que c'est. Que ce soit une LinkedList (suppression et insertion en O(n) par exemple), ou une ArrayList(en temps constant pour ces méthodes), ça ne t'intéresse que lorsque tu compares les performances lors de l'implémentation initiale de ton ensemble. Une fois que c'est fait, tu fais abstraction de cet aspect "bas-niveau" pour te concentrer uniquement sur l'utilisation de ta liste, et donc tu passes par ton interface.
En espérant avoir été un peu plus loin, sans avoir été trop confus...
Cordialement
wiam_
Messages postés
168
Date d'inscription
samedi 1 mars 2008
Statut
Membre
Dernière intervention
11 août 2013
8
27 déc. 2008 à 12:24
27 déc. 2008 à 12:24
merci à vous les deux c'est plus clair maintenant
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
bonjour,
Moi je voulais comprendre d abord quant est ce qu'on utilise des classes abstraites ?
Merci
Moi je voulais comprendre d abord quant est ce qu'on utilise des classes abstraites ?
Merci
KX
Messages postés
16753
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
25 novembre 2024
3 020
17 janv. 2011 à 12:40
17 janv. 2011 à 12:40
Les classes sont abstraites quand elles ne sont pas directement instantiables.
Par exemple si tu représentes le règne animal, la classe Animal sera abstraite, de même que ses classes filles Mammifère, Oiseau, Reptile...
En effet il y certaines caractéristiques qui ne sont pas connues et qui ne peuvent pas être implémentées (par exemple le nombre de pattes qui varie selon l'espèce)
Par contre des classes finales comme Chat, Aigle, Serpent... pourront être totalement implémentées puis éventuellement spécifiées (ChatPersan, AigleRoyal, Vipere...)
Par exemple si tu représentes le règne animal, la classe Animal sera abstraite, de même que ses classes filles Mammifère, Oiseau, Reptile...
En effet il y certaines caractéristiques qui ne sont pas connues et qui ne peuvent pas être implémentées (par exemple le nombre de pattes qui varie selon l'espèce)
Par contre des classes finales comme Chat, Aigle, Serpent... pourront être totalement implémentées puis éventuellement spécifiées (ChatPersan, AigleRoyal, Vipere...)
Moi je suis capable de faire des interfaces sans que ce sois une interface dans le sens propre du terme. Une interface ces une classe abstraite dont toute les méthodes sont abstraites. Très important à savoir car ce n'est pas tout les languages de programmation qui on la notion d'interface(ie. C++) mais simplement la notion d'heritage multiple.
KX
Messages postés
16753
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
25 novembre 2024
3 020
8 mars 2011 à 20:24
8 mars 2011 à 20:24
En Java, classes et interfaces sont deux choses distinctes. Même si les concepts sont similaires, on ne peut pas utiliser une interface à la place d'une classe abstraite ni inversement !
Salut à tous,
Si j'ai bien compris, en java, une Interface sert à s'assurer que toute classe l'implémentant contiendra TOUTES les fonctions définies.
Ce qui évite certainement bien des erreurs.
Exemple : un chien, comme tout être vivant, doit manger, l'utilisateur ne doit donc pas oublier la méthode "manger".
L'héritage sert à ne pas se retaper bêtement toutes les méthodes/attributs.
Les classes abstraites servent à ne pas être instanciées.
Exemple : il est idiot d'instancier un objet "Animal", puisqu'un animal n'existe pas en temps que tel. L'utilisateur doit créer un objet "pigeon", "chien", puisqu'un animal est forcément un pigeon ou un chien (enfin, vous me comprendrez).
CEPENDANT
Si je décidais de faire ma tête de ... et de ne rien appliquer de tout ça, tout marcherait quand même. Je m'explique :
1/ Si je prends un programme (qui fonctionne) avec une interface EtreVivant.
Je vire tous les "implements EtreVivant" ainsi que la-dite interface, tout marchera.
2/ Les "copier-coller" peuvent remplacer l'héritage.
3/ Je vire tous les "abstract" servant à définir les classes abstraites et tout marchera aussi.
Conclusion
Tous ces dispositifs n'ajoutent pas de nouvelles possibilités de programmation. On fait exactement la même chose via les trois solutions donnée précédemment.
En revanche, il y a un intérêt certain dans la clarté et la concision du code : pas la peine d'utiliser directement ma classe abstraite animal, c'est stupide, ce qui tombe bien, puisque le compilateur ne l'acceptera pas.
Ainsi, un programmeur qui utilise mes bibliothèques ne se creuse pas la soupière, il créera un objet "chien".
La grande question est donc bien sûr : AI-JE RAISON ?
Si j'ai bien compris, en java, une Interface sert à s'assurer que toute classe l'implémentant contiendra TOUTES les fonctions définies.
Ce qui évite certainement bien des erreurs.
Exemple : un chien, comme tout être vivant, doit manger, l'utilisateur ne doit donc pas oublier la méthode "manger".
L'héritage sert à ne pas se retaper bêtement toutes les méthodes/attributs.
Les classes abstraites servent à ne pas être instanciées.
Exemple : il est idiot d'instancier un objet "Animal", puisqu'un animal n'existe pas en temps que tel. L'utilisateur doit créer un objet "pigeon", "chien", puisqu'un animal est forcément un pigeon ou un chien (enfin, vous me comprendrez).
CEPENDANT
Si je décidais de faire ma tête de ... et de ne rien appliquer de tout ça, tout marcherait quand même. Je m'explique :
1/ Si je prends un programme (qui fonctionne) avec une interface EtreVivant.
Je vire tous les "implements EtreVivant" ainsi que la-dite interface, tout marchera.
2/ Les "copier-coller" peuvent remplacer l'héritage.
3/ Je vire tous les "abstract" servant à définir les classes abstraites et tout marchera aussi.
Conclusion
Tous ces dispositifs n'ajoutent pas de nouvelles possibilités de programmation. On fait exactement la même chose via les trois solutions donnée précédemment.
En revanche, il y a un intérêt certain dans la clarté et la concision du code : pas la peine d'utiliser directement ma classe abstraite animal, c'est stupide, ce qui tombe bien, puisque le compilateur ne l'acceptera pas.
Ainsi, un programmeur qui utilise mes bibliothèques ne se creuse pas la soupière, il créera un objet "chien".
La grande question est donc bien sûr : AI-JE RAISON ?
KX
Messages postés
16753
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
25 novembre 2024
3 020
28 juin 2011 à 17:40
28 juin 2011 à 17:40
Non ^^
Et puisque la réponse courte ne te conviendra pas je te propose donc la version longue :p
Il y a un gros problème en programmation orientée objet c'est l'héritage multiple.
C'est toujours difficile de prendre un exemple concret, j'espère que tu comprendras quand même.
Soient :
Une classe A avec une méthode fa abstraite
Deux classes B et C, qui héritent de A et implémentent fa, et respectivement fb, et fc.
Une classe D, qui hérite de B et C, donc de fa, fb, et fc.
Problème : quel fa doit on hérité, celui de B, ou celui de C, un mélange des deux ?
En C++ l'ordre d'héritage est important, ainsi hériter de B et C, implique de prendre fa dans B, alors qu'hériter de C et B, implique de prendre fa dans C.
En Java on ne peut pas faire d'héritage multiple, c'est là qu'interviennent les interfaces.
Dans mon exemple, A serait une interface, B et C deux autres interfaces qui héritent de A.
D par contre pourrait-être une interface aussi, mais surtout une classe qui implémenterait donc à la fois B et C, c'est à dire à qui on impose d'implémenter fa, fb, et fc, et du coup on a pas à choisir quel fa on va utiliser, on sera obligé de l'implémenter puisque les interfaces B et C l'imposent...
Je sais pas si c'est clair, j'espère en tout cas ^^
Et puisque la réponse courte ne te conviendra pas je te propose donc la version longue :p
Il y a un gros problème en programmation orientée objet c'est l'héritage multiple.
C'est toujours difficile de prendre un exemple concret, j'espère que tu comprendras quand même.
Soient :
Une classe A avec une méthode fa abstraite
Deux classes B et C, qui héritent de A et implémentent fa, et respectivement fb, et fc.
Une classe D, qui hérite de B et C, donc de fa, fb, et fc.
Problème : quel fa doit on hérité, celui de B, ou celui de C, un mélange des deux ?
En C++ l'ordre d'héritage est important, ainsi hériter de B et C, implique de prendre fa dans B, alors qu'hériter de C et B, implique de prendre fa dans C.
En Java on ne peut pas faire d'héritage multiple, c'est là qu'interviennent les interfaces.
Dans mon exemple, A serait une interface, B et C deux autres interfaces qui héritent de A.
D par contre pourrait-être une interface aussi, mais surtout une classe qui implémenterait donc à la fois B et C, c'est à dire à qui on impose d'implémenter fa, fb, et fc, et du coup on a pas à choisir quel fa on va utiliser, on sera obligé de l'implémenter puisque les interfaces B et C l'imposent...
Je sais pas si c'est clair, j'espère en tout cas ^^
Oui et non...
Je n'a jamais fait de C++ objet, je ne répondrais donc que pour java...
Ce que tu dis ne me paraît pas inexact, mais je ne vois pas en quoi ça me contredit : si je vire A, B et C qui sont des interfaces, je vire également le "implements B, C" dans la définition de D.
D marchera quand-même, non ?
Je n'a jamais fait de C++ objet, je ne répondrais donc que pour java...
Ce que tu dis ne me paraît pas inexact, mais je ne vois pas en quoi ça me contredit : si je vire A, B et C qui sont des interfaces, je vire également le "implements B, C" dans la définition de D.
D marchera quand-même, non ?
KX
Messages postés
16753
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
25 novembre 2024
3 020
28 juin 2011 à 19:14
28 juin 2011 à 19:14
Sauf que l'on peut avoir, et c'est souvent le cas, une classe E, qui définit une méthode pour tous les éléments de A, donc si D n'implémente plus A, B, ou C, on ne peut plus utiliser les méthodes de E sur des éléments de D.
Un bon exemple c'est l'interface Collection et la classe Collections. La classe Collections offre des méthode de manipulation des classes implémentant Collection et ce peu importe quelle soit leur implémentation. Donc on pourra par exemple trier une liste, un arbre, ou n'importe quelle Collection en utilisant Collections.sort. Si tu créés ta propre classe Arbre mais que tu n'implémente pas Collection tu ne pourras pas utiliser ces méthodes là, et tu devras tout redéfinir, alors qu'il te suffit de faire l'implements pour y avoir accès, ainsi qu'accéder à d'autres méthodes d'autres classes.
Tout cela concerne la réutilisabilité, pourquoi tout réinventer à chaque fois ?
Si tu "vires" tout ce qui concerne l'héritage tu perds de vue la programmation objet.
Tu peux tout recoder tout dans un même fichier à grands coups de copier-coller et oui ça reviendra au même... Pour reprendre ton exemple d'animaux, si tu considères que la seule différence entre labrador noir et un labrador sable c'est la couleur, toi tu vas copier-coller toutes ta classe labrador et modifier la couleur, quand un bon programmeur, héritera de la classe labrador et implémentera la classe couleurDePeau...
Un bon exemple c'est l'interface Collection et la classe Collections. La classe Collections offre des méthode de manipulation des classes implémentant Collection et ce peu importe quelle soit leur implémentation. Donc on pourra par exemple trier une liste, un arbre, ou n'importe quelle Collection en utilisant Collections.sort. Si tu créés ta propre classe Arbre mais que tu n'implémente pas Collection tu ne pourras pas utiliser ces méthodes là, et tu devras tout redéfinir, alors qu'il te suffit de faire l'implements pour y avoir accès, ainsi qu'accéder à d'autres méthodes d'autres classes.
Tout cela concerne la réutilisabilité, pourquoi tout réinventer à chaque fois ?
Si tu "vires" tout ce qui concerne l'héritage tu perds de vue la programmation objet.
Tu peux tout recoder tout dans un même fichier à grands coups de copier-coller et oui ça reviendra au même... Pour reprendre ton exemple d'animaux, si tu considères que la seule différence entre labrador noir et un labrador sable c'est la couleur, toi tu vas copier-coller toutes ta classe labrador et modifier la couleur, quand un bon programmeur, héritera de la classe labrador et implémentera la classe couleurDePeau...
comment je fais pour en plus être sur el mailing list de ce forum ?
KX
Messages postés
16753
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
25 novembre 2024
3 020
29 juin 2011 à 14:47
29 juin 2011 à 14:47
Dans les mails que tu reçois tu as un lien "Arrêter les envois de mails concernant cette discussion "
bonjour,
une classe abstraite est une classe qui contient des méthodes abstraites et des méthodes implémenté et la règle dit qu'une classe abstraite doit obligatoirement avoir une méthode implémenté sinon y 'a pas l'utilité d'hérité de cette classe
en faite le but c'est de éviter les répétition des méthode dans les classes
de coup on crie une classe de dans on met les méthode qui se répéte dans toutes les classe ,puis on hérite de cette classe mère (abstraite) pour hérité aussi ces méthodes.
Pub supprimée modération CCM
une classe abstraite est une classe qui contient des méthodes abstraites et des méthodes implémenté et la règle dit qu'une classe abstraite doit obligatoirement avoir une méthode implémenté sinon y 'a pas l'utilité d'hérité de cette classe
en faite le but c'est de éviter les répétition des méthode dans les classes
de coup on crie une classe de dans on met les méthode qui se répéte dans toutes les classe ,puis on hérite de cette classe mère (abstraite) pour hérité aussi ces méthodes.
Pub supprimée modération CCM
1 sept. 2009 à 21:08
Merci de ton avis.
J'avoue avoir été un peu extrême en affirmant que "la factorisation ne sert à rien". J'entendais simplement :
"il ne faut pas créer de classe abstraite uniquement par souci de factorisation".
En effet, dans ce cas, on augmente le risque de bugs en voulant rendre générique du code similaire (certes si tu as 50 fois la même méthode, c'est qu'il y a un problème dans ta conception), et on crée des classes abstraites qui n'ont absolument aucun intérêt ni technique ni fonctionnel, car elles servent juste à centraliser du code.
Pour moi la notion d'héritage est bien plus riche que ça : on doit avant tout hériter d'un type, puis ensuite on hérite (et éventuellement défini ou redéfini) de ses méthodes. C'est d'ailleurs ce que tu expliques dans ton exemple avec les formes : tes objets étendent ta classe abstraite car ils sont similaire d'un point de vue comportement (un carré ou un rectangle c'est une figure 2D, donc elles étendent ta classe Figure2D), et non pas uniquement pour factoriser le code.
Si tu veux uniquement factoriser du code, tu peux utiliser un design pattern Wrapper, ce sera d'autant plus propre que tu ne "perdra" pas ton héritage (dans de nombreux langages objets l'héritage multiple n'existe pas, et étendre une classe abstraite uniquement pour factoriser du code fera que ta classe ne pourra plus étendre de classe par la suite, ce qui peut être gênant si tu souhaites étendre un type et un comportement par la suite).
Cordialement,
7 févr. 2010 à 12:39
je sais que ce post date un peu maintenant, mais pour tous ceux qui tomberaient dessus par hasard comme moi, il peut être utile d'apporter encore quelques précisions.
Une interface, c'est avant tout un contrat, à savoir un ensemble de propriétés et méthodes que la classe DOIT implémenter.
Une classe abstraite, c'est avant tout une classe permettant de factoriser un ensemble de propriétés et de méthodes communes, voire même un constructeur commun.
C'est bien utile dans le cadre de l'écriture de plugins par exemple. Cela permet de définir des méthodes de construction et d'appel communes, permettant ainsi à l'application parente de ne connaitre que les points d'entrée de la classe abstraite.
Pour parler UML :
- L'interface permet de définir les Boundary (frontières) de la classe finale.
- La classe abstraite permet de définir les CU (Cas d'utilisation) simplifiées.
- La classe finale permet de définir les CU optimisées, prenant en compte les CU abstraites et concrètes.
Modifié par Satelite01 le 22/02/2011 à 10:47
Je me permet de remonter ce poste... Car je suis, disons le, assez choqué par l'intervention de "Marco la baraque"!
Je reprend vos termes..
***
En effet, dans ce cas, on augmente le risque de bugs en voulant rendre générique du code similaire (certes si tu as 50 fois la même méthode, c'est qu'il y a un problème dans ta conception), et on crée des classes abstraites qui n'ont absolument aucun intérêt ni technique ni fonctionnel, car elles servent juste à centraliser du code.
***
Je donne un exemple simple d'implémentation, juste technique, de centralisation de code avec classes Abstraites...
J'ai besoin de définir des objets pour un projet personnel comme tel : Les données ci dessous sont à titre d'exemple...
Objet A : variables : String name, String phoneNumber
Objet B : variables : String name, String insuranceNumber
Objet C : variables : String name, ImageIcon image, String productId
Objet D variables : String name, ImageIcon image, String comment
Cas avec factorisation via classes Abstraites ca donne :
Toutes les classes ci dessous ont juste les getter et setter pour les variables qu'elles portent en public
Abstract class AbstractBeanWithName : String name
Abstract class AbstractBeanWithImage extends AbstractBeanWithName : ImageIcon image
BeanWithPhoneNumber extends AbstractBeanWithName
BeanWithInsuranceNumber extends AbstractBeanWithName
BeanWithProductIdNumber extends AbstractBeanWithImage
BeanWithCommentNumber extends AbstractBeanWithImage
- Les variables ont le même nom partout, uniformité...
- Si je veux savoir quels objets ont une image, je recherche les références à AbstractBeanWithImage (gain de temps énorme)
- Gain de place énorme.. si on connait son architecture, on sait de suite quelles variables sont héritées
- Maintenabilité parfaite..., besoin d'une vérification de saisie sur une variable... je peux la mettre au niveau du setter, exemple bidon mais probant, une seule méthode à retoucher
-- j'ai besoin un jour d'un objet avec juste un nom, je retire Abstract de ma classe AbstractBeanWithName qui devient BeanWithName... , même chose si un jour besoin d'un objet avec nom et image... AbstractBeanWithImage devient BeanWithImage
- Si un traitement est particulier à l'un des objets... par exemple le nom sur BeanWithPhoneNumber et qu'il existe déjà un traitement sur ce setter que tous les autres objets utilisent, l'override du setter est possible... Cas super rare si la spec est bien faite au départ... l'analyse, mais une seule méthode à créer...
Cas sans facto :
Object A : variables : String name, String phoneNumber
Object B : variables : String name, String insuranceNumber
Object C : variables : String name, ImageIcon image, String productId
Object D variables : String name, ImageIcon image, String comment
- Les variables peuvent finir avec des noms différents.. ex : nom, name, data_name (ce qui arrive souvent, si peu de factorisation du code... et ca devient totalement illisible)
- Je dois ouvrir toutes les classes pour savoir lesquelles portent une image... puisque chaque classe pouvant être codée par des dev différents, les noms des variables peuvent changer...
- Classe 4 fois plus grande que dans l'autre cas... donc scrolling... et il faut ouvrir tout pour savoir qui porte quel type de variable, méthodes, même si identique (name) (image)
- Maintenabilité faible, si une modification est à reporter partout, cas de la vérification de saisie sur un setter, je dois parcourir toutes les classes et faire un copier collé bête et méchant (perte de temps, risque d'oubli, sans compter le noms des méthodes et des variables qui peuvent changer) setNom, setName etc....
-- j'ai besoin un jour d'un objet avec juste un nom, je rajoute une classe... risque de changer de nom de variable... etc etc , même chose si un jour besoin d'un objet avec nom et image...
- Si un traitement est particulier à l'un des objets... je l'implémente dans son coin
Bref, d'expérience, sur les projets non factorisés... sans aller dans l'extrême que j'ai exposé... Qui reste cependant totalement valable et probant, puisque là, c'était juste une factorisation purement technique... On obtient :
- Des classes codées par des devs différents, qui font la même chose... car manque de connaissance de l'architecture du projet
- Des variables à l'utilité identique... String nom, qui changent name, nom, data_name, illisible, impossible à facilement retrouver dans le code, à part tout parcourir
- Problème de performance, car certaines méthodes ayant le même but, la même fonction, vont changer de nom, d'algorithme, impossible à retrouver... parfois on fait une correction dans un coin et oh zut, un abrutis à fait la même chose avec un autre nom, seulement, je dois me taper 40 lignes de code, pour bien vérifier que c'est la même
La factorisation est l'a-panache de la perfection en programmation...
Gain de temps, Maintenabilité, lisibilité --> GML lol
***
Pour moi la notion d'héritage est bien plus riche que ça : on doit avant tout hériter d'un type, puis ensuite on hérite (et éventuellement défini ou redéfini) de ses méthodes. C'est d'ailleurs ce que tu expliques dans ton exemple avec les formes : tes objets étendent ta classe abstraite car ils sont similaire d'un point de vue comportement (un carré ou un rectangle c'est une figure 2D, donc elles étendent ta classe Figure2D), et non pas uniquement pour factoriser le code.
***
Vrai et Faux...
Vrai, si vous instanciez Figure2D... uniquement! et donc que celle ci ait une raison de l'être!
Faux, dans le sens ou... une classe abstraite trouve son utilité quand vous avez des objets dont l'implémentation de certaines méthodes différent... dessiner() comme l'a si bien exposé "misterETS", mais que des méthodes et/ou variables sont communes static ou pas...
Si Figure2D, n'est qu'un agrégat de méthodes et variables communes aux objet à dessiner en 2d, mais que chacun d'eux se dessine différemment, Figure2D n'a aucune raison d'être instanciée en temps que tel, puisqu'elle ne porte que la description de la méthode dessiner... Figure2D ne dessinant rien en soit même, on la met en Abstract... Logique des plus basique!
CQFD : Une classe abstraite est donc par nature une classe permettant de factoriser du code et de permettre tout de même une divergence de comportement de ses enfants... C'est donc un outil très puissant, mais qu'il faut savoir manier... Ce qui est encore appuyé par le fait que les variables définies dans une classe abstraite peuvent être public, protected, private en opposition à une interface dont les variables ne peuvent être que public...
Ainsi que par le pattern Template...
Une classe abstraite est aussi et comme son nom l'indique, utile pour abstraire un code client des spécificités des classes filles de cette classe abstraite...
Si une méthode reçoit en argument une classe concrète (instanciable) dans un code "client", toute modification des particularités de cette classe seront à prendre en compte dans le code "client", ainsi que si il y a ajout d'une classe concrète de la même famille...
Si maintenant celle ci reçoit en argument la Classe abstraite mère d'une famille de classes concrètes... le code client s'abstrait de leur particularités, sera plus facilement maintenable et la modification, création de classes concrètes de cette même famille (héritant la même classe abstraite passé ici en argument) ne demandera retouche du code client, que si, il y a ajout de nouvelle fonctionnalités (méthodes) utiles... dans son cas ;) (Pattern Abstract Factory)
****
Si tu veux uniquement factoriser du code, tu peux utiliser un design pattern Wrapper, ce sera d'autant plus propre que tu ne "perdra" pas ton héritage (dans de nombreux langages objets l'héritage multiple n'existe pas, et étendre une classe abstraite uniquement pour factoriser du code fera que ta classe ne pourra plus étendre de classe par la suite, ce qui peut être gênant si tu souhaites étendre un type et un comportement par la suite).
****
Le design pattern Wrapper est très proche de l'adapter et au final est plus proche de la spécialisation que de l'héritage, donc à l'opposer fondamentalement de la factorisation de code...
De plus, je rappel que les classes abstraites peuvent hériter d'autre classes.... Votre explication ne fait que malheureusement souligner votre manque de connaissances en la matière et risque justement d'induire en erreur des développeurs et de provoquer une faible maintenabilité, ainsi qu'un nombre de classes exponentielles... par rapport au besoin!
En espérant éclaircir le sujet pour les futurs lecteurs, a bon entendeur ;)