C'était qu'un vulgaire exemple...
C'était qu'un vulgaire exemple...
Mais c'est un exemple qui en dit long...
Tu dois réfléchir à tes classes en termes de comportements (de service qu'elle peuvent rendre, ou de messages qu'elle peut recevoir).
Les membres de tes classes sont donc à envisager comme les variables qui permettront de rendre le service attendu (d'appliquer le comportement voulu).
En ce sens, un setXXX ne représente absolument pas un comportement "cohérent", "utile" ou ne serait-ce que auto commenté, et c'est donc quelque chose à proscrire
setNombresLitreEssence serait par exemple pour le moins ambigu: s'agit il de remplir le réservoir, ou de le changer (et donc de modifier sa capacité) , alors que remplirReservoir et remplacerReservoir (si ce deuxième existe) ne pourraient en aucun cas être pris l'un pour l'autre
Reste à ne pas filer l'accès à des classes critiques à n'importe qui...
Pour le reste, on fait avec ce que l'on a : si C++ Builder permet le concept de propriétés (moins proprement qu'en Delphi, par contre), ce n'est pas le cas de la plupart des compilateurs C++ vu que ça ne fait pas partie du langage, c'est une extension propre à Borland. C'est dommage, mais on fait avec et on pallie...
Par rapport au débat, il reste néanmoins évident qu'une méthode "inutile" est, justement, inutile et n'a pas lieu d'être. Reste à savoir si c'est une erreur de conception (fréquent), ou une contrainte d'implémentation (ex : variables read ou write-only)... Mais c'est souvent le signe que tu n'as pas assez abstrait ta classe. Ce genre de code "inutile" peut se comprendre sur une classe "wrapper", mais c'est moins évident pour une classe "terminale".
Pour ma défense, j'ai juste voulu citer un cas ou une valeur accédée par un getter/setter était soudainement stockée différemment, qu'elle soit dans un objet membre, une map ou une espèce de sous-entité quelconque sans qu'on veuille modifier la façon dont les autres composants du projet interagissent avec la classe.
Par ailleurs, en java ou en .Net (technos avec lesquelles j'ai le plus d'expérience professionnelle) il est largement admis comme standard de ne jamais laisser un membre être accédé publiquement.
Ne pas faire cela peut empêcher de fonctionner de nombreux frameworks qui utilisent l'introspection, par ailleurs on a l'assurance que les getters setters pauvres finissent inlinés en exécution sauf dans certains cas de polymorphismes.
Actuellement sur le net, en C++, je vois autant d'avis "pour" que "contre", la seule chose dont je sois sûre étant que si les membres sont en accès publics et que l'on souhaite modifier le fonctionnement interne ou ajouter des garde-fous sans changer la façon d'interagir avec l'objet, ce sera plus délicat.
Je ne suis pas en train de vous dire que vous avez tort, juste que je suis de plus en plus persuadé qu'il y a pas de réponse parfaite à ce débat. Juste des préférences, des habitudes et des philosophies.
Ce que je critique, et ce contre quoi je mets en garde, c'est ce mouvement "nous, nous encapsulons, nous mettons des setters" -- au même titre que je sors des "le downcasting est rarement bien venu".
Ce n'est pas une véritable encapsulation, tout au mieux on positionne des contraintes sur des propriétés. Avec des setters, on donne une vision complète sur les mécaniques internes de la classe : on pense en termes de données que l'on permet aux extérieurs de modifier.
Attention, je ne suis pas un "tout public" pour autant vu que la mécanique interne est toujours exposée. La "correction" que j'ai proposée à l'exemple correspond à ce que je cherche à systématiquement privilégier : le service rendu.
Et cet exemple est justement typique. Car beaucoup de setters sont des palliatifs à une bonne conception OO.
Et nous sommes globalement bien d'accord là dessus...
Seulement, un simple setter démontre bien souvent que l'on n'a pas assez abstrait l'idée de l'utilité d'un membre.
Il est bien évident qu'à choisir, je préfère laisser un membre privé (entre autres parce que cela facilitera énormément la maintenance le jour où je déciderai de le représenter différemment), mais, dés lors, il semble cohérent, plutot que d'utiliser un setMachinChose et un getMachinChose, de définir un comportement qui soit réellement en rapport avec le comportement observé.
Dans le cas de ton véhicule, ce serait remplireReservoir et quantiteCarburant, par exemple...
Cela ne t'empêcherait absolument pas de les faire agir comme des getter / setter simple (il n'y a que le nom de la fonction qui change, en définitive), mais cela te permettrait, le jour où tu décide de modifier la manière dont les données sont représentées ou les vérifications à effectuer avant de valider l'action, de garder un comportement dont le nom est cohérent par rapport à ce qui est réellement effectué
Si, pour une raison ou une autre, tu en viens à ne pas pouvoir déterminer le terme représentant ce comportement par autre chose que getXXX ou setXXX, il est sans doute temps de se poser la question de l'utilité réelle du membre dans ta classe...
Et, si, pour une raison ou une autre, tu en viens à te dire que, de toutes manières, la manière dont ce membre sera représenté ne changera absolument pas et / ou qu'il n'y aura jamais de test de cohérence concernant les modifications apportées "depuis l'extérieur" (ou qu'elles ne seront tout simplement pas admises), il n'y a aucune raison à pratiquer l'encapsulation à tout prix (quitte à déclarer le membre constant s'il ne doit pas être modifié )
C'est peut être le cas...Je ne suis pas en train de vous dire que vous avez tort, juste que je suis de plus en plus persuadé qu'il y a pas de réponse parfaite à ce débat. Juste des préférences, des habitudes et des philosophies.
La philosophie sur laquelle nous nous basons est celle qui consiste a voir l'objet au départ des services qu'il peut rendre ou des messages qu'il peut transmettre ou recevoir, et je reste convaincu que c'est la moins mauvaise des approches
Et des performances et/ou des réductions de bugs...
Axiome : Plus le nombre de lignes de codes est élevé, plus il y aura de bugs dedans, et plus la probabilité d'en oublier un est élevée.
Axiome : Tout code inutile (code mort et/ou redondant, ce qui n'inclus pas le code de conservation d'intégrité bien sûr) est une source de bugs et/ou de perte de performances, et doit donc être supprimé.
C'est pour ça que tu verras des "pro-accesseurs" en Java/C# (langages "lents", voire "poussif" dans le cas de Java... ), et des avis mitigés en C++ (utilisé aussi bien pour du code "lent" que pour du code "performant", d'où deux sons de cloche).
Les "pro-accesseurs" ne comprennent pas la notion de performance (ni de partage des ressources de la machine, parfois), les "anti-accesseurs" réfléchissent d'abord en terme de performances.
Si tu commences à aller vers le code sécuritaire, c'est encore pire : la réduction de bugs est primordiale, donc les accesseurs inutiles sautent sans remords.
De toutes façons, faut bien voir qu'aucun accesseur ne te protègera contre un débordement de mémoire qui ira gentiment jardiner les attributs d'après, foutant en l'air toutes les "protections" que tu pensais être suffisantes... Et laisser les données de classe "visibles" de façon aussi flagrante est idéal pour aller se faire jardiner.
Tu es mauvaise langue Mac Lak. Même dans la communeauté Java il y a un mouvement pro-OO qui dénonce les accesseurs/mutateurs ou même les singletons.
(J'admets volontiers que le code code mort est le premier truc qui es venu me titiller explicitement)
Ah ? Si c'est le cas, je vais peut-être finir par trouver une appli Java réagissant à une vitesse décente... Ce qui n'est pas trop tôt !
Ceci étant dit, sont trop silencieux, ces braves gens : faudrait qu'ils se manifestent un peu plus... Ne traînant que rarement du côté de la communauté Java, je n'avais JAMAIS entendu parler d'un tel mouvement, par contre j'entends tous les jours ou presque des annonces d'un n-ième framework "plus mieux que les autres"...
Luc Hermitte, Mac LAK, puis-je vous demander de ne pas trop digresser sur les habitudes java (ou pire, sur l'intérêt comparé des différents framework java), même si elles ont été abordées par le PO lui-même
_skip: Quelque part, je crois que le problème est que, bien souvent, on n'apprend la POO qu'au travers d'un langage particulier (le premier langage OO que l'on commence à manipuler sérieusement)...
Du coup, on finit "fatalement" par prendre certaines habitudes propres à ce langage sans *forcément* comprendre réellement la POO ou les écarts que de telles habitudes peuvent avoir par rapport au concept OO.
Et il devient alors particulièrement difficile, lorsque l'on se tourne vers un autre langage, de décider de renoncer à ces habitudes, malgré le fait qu'elles se puissent s'avérer être mauvaises ou inadaptées
Il est vrai que je rêve de voir se généraliser des méthodes d'enseignement dans lesquelles nous apprendrions l'algorithmie et l'OO sans écrire une ligne de code avant de passer sur un langage quelconque pour voir comment il met les différents concepts en oeuvre (même si, de ce point de vue, je suis un privilégié qui a passé plus de trente heures à apprendre l'algorithmie avant d'écrire sa première ligne de code )
Mais là aussi, je digresse, et il n'est donc pas utile de répondre à tout
Je parle de mouvement OO. Pas de vitesse. Je laisse ce débat à l'autre troll déjà bein nourri avec ses 105 pages.
En fait, en cherchant des références sur la pertinence des setters pour alimenter un guide orienté qualité, j'étais tombé sur plusieurs articles et des discussions dans des forums java à ce sujet. Mouvement que j'avais perçu en réaction à la tendance un attribut => une paire get/set qui semblait venir de je ne sais trop quel framework.
Il faut avouer que certains EDI (je pense, entre autre à eclipse) ont entre autres la sale manie de te prévenir que tu n'a pas de mutateur / accesseur pour un membre si tu ne crées pas explicitement une méthode getNomMembre / setNomMembre (et ce, malgré qu'il soit "déguisé" sous la forme de faireCeci recupererCela )
La première chose que j'ai faite en sachant que j'allais devoir faire du C++ *non scolaire*, ça a été d'acheter le bouquin "effective c++". Je suis entièrement prêt à remettre en question mes habitudes, mais ce n'est pas facile sur un thème ou visiblement il y a deux philosophies différentes avec chacun ses partisans, et chacun de bonnes raisons._skip: Quelque part, je crois que le problème est que, bien souvent, on n'apprend la POO qu'au travers d'un langage particulier (le premier langage OO que l'on commence à manipuler sérieusement)...
Du coup, on finit "fatalement" par prendre certaines habitudes propres à ce langage sans *forcément* comprendre réellement la POO ou les écarts que de telles habitudes peuvent avoir par rapport au concept OO.
Et il devient alors particulièrement difficile, lorsque l'on se tourne vers un autre langage, de décider de renoncer à ces habitudes, malgré le fait qu'elles se puissent s'avérer être mauvaises ou inadaptées
Ce qui est mauvais ou inadapté ne l'est pas forcément pour toutes les situations, je crois que c'est une question d'acquérir la capacité d'émettre un jugement pertinent, et souvent celle-ci passe par une connaissance relativement profonde de la techno employée et des implications.
Pour avoir fait toutes mes études en cours à 50% avec un vrai job à coté, je n'ai pas une haute opinion du monde académique car j'ai pu m'apercevoir de la profondeur du gouffre qu'il y a parfois entre la théorie toute mimi et le terrain sur lequel les exigences sont réelles et nécessitent des compromis.Il est vrai que je rêve de voir se généraliser des méthodes d'enseignement dans lesquelles nous apprendrions l'algorithmie et l'OO sans écrire une ligne de code avant de passer sur un langage quelconque pour voir comment il met les différents concepts en oeuvre (même si, de ce point de vue, je suis un privilégié qui a passé plus de trente heures à apprendre l'algorithmie avant d'écrire sa première ligne de code )
J'estime que ce que je sais aujourd'hui, je le dois plus à mon parcours professionnel et à toutes ces nuits blanches de recherche personnelle animée par ma seule passion qu'à mes études.
@Mac LAK
Ben tu vois ici, nous avons du admettre du C++ dans notre application serveur en java. Pour deux raisons, d'une part l'occupation mémoire trop importante en java à cause du boxing des primitifs dans les collections et les maps, de l'autre pour limiter la rétro-ingénieurie de nos algos qui servent à la création des structures sur lequel notre service repose pour répondre aux requêtes.
Java est très riche pour ce qui est serveur, interaction avec les BDD, et la sécurité du code est élevée. Par contre, de l'autre côté de la balance il y avait les problèmes qui nous ont poussé à utiliser c++ pour certaines parties critiques.
Pour l'instant la crème prend assez bien, ces deux technos peuvent aussi se compléter... J'aurai pu parler de ce cas sur le topic débat C++ vs java mais il est déjà parti en sucette depuis longtemps. Je me demande parfois si les gens seraient prêts à admettre qu'il y a des situations différentes, des solutions différentes et des outils différents qui peuvent être choisis au cas par cas sans devoir nécessairement être strictement supérieurs ou inférieurs.
(désolé du triple post)
@tous,
pendant que j'y suis, merci pour avoir discuté courtoisement de tout ça avec moi.
C'est comme ça que je conçois un forum.
@_skip : l'intégration de code "natif" à Java (ou C#, Python, Lua, ...) est plus que courante, pour deux raisons primaires :
- Ne pas réécrire un code déjà testé, validé et fiable.
- Donner un coup de performance sévère à un langage "poussif", du moins selon des critères qui sont "un peu" au delà des normes du quidam moyen en monoposte...
Un excellent outil pour réaliser cette opération est SWIG, qui se charge d'écrire tout seul les wrappers entre du C/C++ et un autre langage. Java fait partie des langages supportés nativement par SWIG (la liste actuelle est : AllegroCL, C# - Mono, C# - MS .NET, CFFI, CHICKEN, CLISP, Guile, Java, Lua, MzScheme, Ocaml, Octave, Perl, PHP, Python, R, Ruby, Tcl/Tk).
Le gros avantage est qu'il est possible d'automatiser intégralement le wrapping, c'est à dire de reconstruire automatiquement le wrapper en cas de modification de l'interface C/C++.
Mais, par rapport à un autre débat actuel, je suis ravi de voir que tu as pu tomber "de visu" sur un cas où les performances se sont faites cruellement sentir... Cela vaut toutes les explications théoriques possibles. Et cela te permet de comprendre pourquoi le "100% haut niveau" possède des limites qui sont très vite atteintes dans un environnement un minimum "sérieux" côté charge de la machine et vitesse de traitement.
Avec un peu d'expérience, on pourrait dire que c'était "prévisible", mais parfois, c'est une évolution du cahier des charges par le client qui impose une amélioration lourde du projet en terme de performances alors que le développement est déjà fort avancé... Le plus courant étant l'application monoclient-monoposte qui se transforme en multiclient-multiposte, d'ailleurs.
C'est un développement orienté service. Par devant c'est une architecture proche d'un webservice RESTful basé sur de simples requêtes http GET. Il semblait qu'un langage tel que java était très bon pour faire ça, particulièrement pour sa capacité multi-plateforme et pour les nombreuses technologies serveurs qu'on a à disposition gratuitement.Mais, par rapport à un autre débat actuel, je suis ravi de voir que tu as pu tomber "de visu" sur un cas où les performances se sont faites cruellement sentir... Cela vaut toutes les explications théoriques possibles. Et cela te permet de comprendre pourquoi le "100% haut niveau" possède des limites qui sont très vite atteintes dans un environnement un minimum "sérieux" côté charge de la machine et vitesse de traitement.
Avec un peu d'expérience, on pourrait dire que c'était "prévisible", mais parfois, c'est une évolution du cahier des charges par le client qui impose une amélioration lourde du projet en terme de performances alors que le développement est déjà fort avancé... Le plus courant étant l'application monoclient-monoposte qui se transforme en multiclient-multiposte, d'ailleurs.
On s'est pas vraiment trompé en fait, la partie client a été très vite mise sur pieds au moyen de quelques servlets, on a profité de JSF pour faire un panneau de contrôle utilisateur sympa par devant.
Le problème est pas tant la partie serveur de notre architecture mais nos algos de similarité. Nous reconstruisons chaque jour pendant la nuit une structure de donnée en forme de réseau sur laquelle on s'appuie ensuite pour servir les requêtes de nos clients (qui doivent durer moins que 10ms en exécution d'où la nécessité de beaucoup charger de choses en mémoire).
La construction de ce réseau, est du gros brassage de matrice, et les temps dépassent fréquemment les 45min.
Le souci n'est pas vraiment la vitesse en elle même mais surtout le fait que l'occupation mémoire due au boxing dans les différentes collections java (LinkedHashMap, ArrayList etc...) ajoutée à celle du serveur... Ca commence à faire trop.
Nous pourrions gonfler un peu ces serveurs mais ceci fait partie de notre stratégie commerciale de mettre en avant le fait que ça tourne sur une infrastructure modeste, et non pas uniquement sur des monstres à 25000 USD.
Boxer un boolean primitif en Boolean objet, ça fait passer son footprint de 1 byte à 16 bytes (8 bytes d'en-tête + 1 pour le boolean primitif boxé + 7 pour compléter un alignement sur 8) et en plus ils sont immuables. C'est acceptable dans pas mal de situations si les objets boxés sont pas trop nombreux et n'ont pas une durée de vie très longue, malheureusement au fur et à mesure que l'on complexifie nos algos, on de plus en plus besoin de grosses structures de données en mémoire.
J'ai souhaité considérer l'utilisation d'une autre stratégie que celle proposée par java en utilisant de véritables collections de primitifs : http://commons.apache.org/primitives/
Malheureusement un grand nombre des structures de données qui me sont utiles n'y figurent pas, je ferai sans doute que repousser un petit peu les limites actuelles.
D'ou cette décision difficile de porter nos algos critiques de java vers C++, ça résout également le problème de la décompilation facile de code java. Si nous perdons ces algos qui sont des années de recherche, c'est fini pour nous.
N'oublie pas que le terme "performances", bien que la plupart du temps pris dans le sens "charge CPU et vitesse d'exécution", recouvre également la notion de consommation mémoire, BP réseau, etc.
Pour un client, c'est souvent négligeable comme problème (sauf à faire vraiment du code de goret). Pour un serveur, la limite arrive bien plus vite.
Effectivement j'oublie d'inclure la consommation mémoire lorsque j'utilise le terme "performances".
Je crois que tu as mal compris (que je l'ai surtout très mal exprimée, en fait) ma pensée...
Je suis très bien placé pour savoir qu'il y a souvent un gouffre entre la théorie et la pratique et j'ai pleinement conscience du fait que la théorie seule ne sert strictement à rien...
Mais je vois la théorie (tous langages confondus) comme un "fil d'ariane" auquel se raccrocher lorsque la réflexion "simple" ou les circonstances font que l'on se retrouve perdu.
A ce titre, j'estime que, si tu apprends dés le départ à présenter correctement un algorithme, que tu arrive à avoir une idée précise de ce que peut être une boucle, un test (éventuellement multiple), une pile, une file, une liste (éventuellement doublement chainée) ou un arbre binaire / n-aire puis que tu termine en apprenant ce qu'est la conception orientée objet et les "lois" qui la régissent sans "polluer" la compréhension de tout cela par des considérations propres à un langage particulier, il te devient beaucoup plus facile de t'adapter en définitive à n'importe quel langage "simplement" parce que tu n'a plus qu'à prendre, justement, ces considérations en compte.
Il te sera alors "facile" de repérer où le langage s'écarte de la théorie et de comprendre les conséquences que cela peut avoir.
Tu sera alors également en mesure de comparer "objectivement" différents langages afin de choisir celui qui présentera le moins de faiblesses (à défaut d'être le meilleur) pour un travail ou un objectif donné
Après, il est certain que pour arriver à maitriser le langage (comprend: pour arriver à une connaissance correctede ses arcanes), rien ne vaut la pratique -- pour ne pas dire les erreurs que tu fera (fatalement ) en l'utilisant ...
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager