Le Fri, 02 Dec 2011 21:45:06 +0100,
>> J'ai écrit des projets de millions de lignes en procédural avec
>> comme seules variables globales celles qui sont dans les
>> gestionnaires de signaux. Procédural ne signifie absolument pas
>> qu'il y a des vrais bouts de variables globales dedans.
>
> Excusez-moi, j'ai oublié "mal fichu", entre "procédural" et "où il y a
> des milliers...". Evidemment que tous les codes procéduraux ne sont pas
> de cette nature.
Et heureusement.
>>> et du code objet bien fichu (très grande
>>> modularité avec des dizaines de milliers de classes, comme j'en ai vu,
>>> utilisation judicieuse du polymorphisme, etc.), je choisis sans hésiter
>>> le code objet. C'est beaucoup plus facile.
>>
>> Pas à la relecture. Un code objet est évident pour celui qui le
>> pond. Pas pour celui qui le relit.
>
> Pas du tout. J'ai fait des audits de codes objets dans lesquels je
> n'avais pas écrit une ligne. Il y en a que je comprenais très aisément.
Comprenez-vous la différence entre 'il y en a que je comprenais très
aisément' et 'je les comprends tous très aisément'. Effectivement,
il y en a.
<snip>
>>> Il est vrai que le code objet est plus "atomisé", justement parce qu'il
>>> est censé être plus modulaire
>>
>> J'aimerais bien qu'on m'explique sans rire pourquoi.
>
> J'ai expliqué pourquoi :
> 1) Une classe = une abstraction
Mouais.
> 2) Modularité = faible couplage entre classes + forte cohésion de chaque
> classe
Mouais.
> 3) encapsulation (qui participe au faible couplage)
Rien à voir avec un code objet. L'encapsulation se fait aussi en
Fortran77 qui n'est à ma connaissance pas objet pour un sou.
> C'est justement l'ensemble de tous ces principes qui fait que l'on sait
> exactement où faire les modifications et détecter plus rapidement
> l'origine des défauts.
Non. Là, aujourd'hui, je bosse sur un code (plus exactement, je
passe après une équipe de developpeurs qui est partie) comportant
plusieurs milliers de classes avec du polymorphisme, des templates
et des tas de choses typiques du C++, de celles que les programmeurs
fous mettent partout pour se faire plaisir. Personnellement, je suis
freelance et je dois dire aussi que je passe après plusieurs autres
qui ont jeté l'éponge. Ça fait maintenant deux mois que je me tape
de la relecture de code. Toutes les classes sont bien écrites, mais
avec des héritages dans tous les sens et des surcharges du même
tonneau. Au final, tu as vraiment du mal à voir qui fait quoi
d'autant que le coup du 'virtual' en C++ est un truc
particulièrement piégeux.
Il y a des défauts dans le code, un tas de défaut de conception
(variables d'instances qui ne sont pas dans la bonne classe) ou
défaut plus graves (entre autres fonctions virtuelles qui sont écrasées
par celles de la classe fille, mais il faut appeler récursivement
toutes celles des parents), et je ne suis pas sûr, mais alors vraiment
pas sûr que je prenne moins de temps à corriger le truc qu'à réécrire
le code 'from scratch'. Et je ne parle que du côté compréhensible du
code, pas encore de l'organisation des classes.
>>> (au sens objet du terme : faible couplage
>>> entre classes ou modules et forte cohésion interne de chaque classe ou
>>> module. Une classe est censée représenter et implémenter une et une
>>> seule "abstraction" au sens de Grady Booch).
>>> Cependant si l'on respecte les principes de l'approche objet :
>>> abstraction, encapsulation, modularité (faible couplage, forte cohésion)
>>> et hiérarchie (agrégation, composition et héritage) alors la maintenance
>>> d'un code objet est extrêmement plus facile que celle d'un code
>>> impératif qui se présente bien souvent comme un vrai plat de spaghettis.
>>> Dire que "il est plus facile de voir les enchaînements logiques dans le
>>> détail dans un code procédural" me fait penser aux programmeurs en
>>> assembleur qui reprochaient à Fortran ou Cobol de ne pas montrer
>>> l'utilisation des registres et que, donc, on ne voyait pas comment le
>>> programme fonctionnait.
>>
>> Sauf que lorsque tu as un gros projet avec des millions de lignes en
>> C++, des héritages dans tous les sens,
>
> Il n'y a pas d'héritage dans tous les sens puisque les classes
> participant à l'héritage sont organisées en hiérarchie.
Quand je dis héritages dans tous les sens, c'est naturellement en
hiérarchie. Ne faites pas semblant de ne pas avoir compris.
Lorsqu'il y a plusieurs milliers de classes qui héritent les unes
des autres (et certaines de plusieurs classes mères), le résultat
peu être assez rigolo à comprendre.
> De plus
> l'héritage (comme la composition) respecte les propriétés d'une relation
> d'ordre stricte. Il n'y a jamais d'héritage "transversal" entre des
> hiérarchies d'héritage différentes.
Où ai-je prétendu le contraire ?
>> des templates en veux-tu-en-voilà,
>
> Les templates servent à la réutilisabilté et l'héritage à la
> factorisation. Ce n'est pas pareil. Les templates sont essentiels pour
> ne pas dupliquer du code par copier/coller ce qui fabrique du code non
> maintenable s'il y a un défaut dans le code dupliqué, comme je l'ai vu,
> jusqu'à une dizaine d'exemplaires dans des fichiers différents.
Certes, les templates, ça peut être pratique. Ça ne concourt
généralement pas à la lisibilité du code.
>> je te mets au _défi_, de voir assez finement qui fait quoi
>> et de mettre tes modifications au bon endroit.
>
> Je l'ai fait pendant presque 10 ans dans du code objet et je n'ai jamais
> eu de souci. Il faut que la phase de conception UML soir impeccable. Si
> ce n'est pas le cas, alors, oui, les tâches de modifications peuvent
> devenir difficiles.
Ça fait un peu plus de dix ans que je fais ça (certes pas à plein
temps). Je n'ai encore jamais vu un code objet, quitte à me répéter,
qui soit facilement maintenable en l'absence de l'équipe de dev
initiale. Comme vous le signalez, il faut d'une part que la
modélisation soit parfaite, mais il faut aussi que le code soit
documenté. La plupart du temps, la seule documentation à jour à ma
disposition est le code lui-même.
>> Lorsque tu as passé une grosse semaine à essayer de comprendre comment tel bout
>> de code fonctionne parce que tu as devant toi une poupée russe
>> d'objets, tu termines invariablement par faire un truc à l'arrache
>> parce qu'il faut bien que ça fonctionne.
>
> Mettre une semaine à comprendre un bout de code objet montre une
> méconnaissance de l'approche objet. Tu parles de "poupées russes
> d'objets" mais c'est toujours comme cela quand le code (et la
> conception) est bien fichu.
Merci de ne pas me prendre pour un débutant. Encore une fois, je me
permets de critiquer l'approche objet parce que je la connais
parfaitement. Le côté poupée russe est intrinsèque au modèle objet,
c'est une évidence. En revanche, lorsque la modélisation est
bancale, les poupées russes sont inextricables.
>> J'ai personnellement bossé sur du code procédural qui avait déjà quarante ans de vie derrière
>> lui et qui était impécable à tous les points de vue. Je n'ai encore
>> jamais vu un code objet un peu ancien irréprochable. Je ne dis pas
>> que ça n'existe pas, simplement que je n'en ai jamais vu.
>
> Moi j'en ai vu beaucoup mais c'est dans des domaines sensibles :
> missiles, avionique, satellites de communication, transport d'énergie
> principalement où la fiabilité est le facteur qualité numéro un.
Tiens, justement, j'ai bossé dans le secteur des satellites, des
missiles et actuellement pour le transport d'énergie. Les autres, je
ne connais pas donc je n'en parlerais pas. Dans ces trois domaines,
j'ai rarement vu du code lisible (je n'ai pas dit qu'il n'était pas
fiable, hein) et maintenable facilement.
>>>> Lorsqu'on arrive sur un programme qu'on doit maintenir et qu'on n'a pas écrit,
>>>> ça fait une grosse différence. Personnellement, j'hésite toujours à
>>>> accepter un boulot consistant à rajouter des fonctions dans un gros
>>>> code objet, on ne sait jamais sur quoi on va tomber (même lorsque le
>>>> code est a priori propre).
>>> En fait, vous semblez avoir peur de l'approche objet puisque vous dites
>>> : "même lorsque le code est a priori propre".
>>
>> Je n'ai pas peur de l'approche objet, je trouve après pas mal
>> d'années à naviguer entre l'objet et le procédural que l'approche
>> objet n'est destinée qu'à avoir des foultitudes de programmeurs pas
>> cher sur le marché et qu'au final c'est une mauvaise réponse à une
>> bonne question. Je peux développer si nécessaire.
>
> Oui développe parce que des programmeurs pas chers dans les applications
> écrites en objets pour les domaines que j'ai cités, je n'en ai pas vus
> beaucoup...
Aujourd'hui, dans une boîte que je ne citerai pas qui travaille dans
le transport d'énergie, le tarif journalier du freelance est de 250
euros HT par jour. En région parisienne. Je te laisse conclure
quant à la qualité du boulot.
Maintenant, l'objet, c'est un paradigme qui fait que les procédures
procèdent des données et non le contraire comme dans les approches
fonctionnelles ou procédurales. Dit autrement, cela revient à
laisser tomber l'éternelle question de savoir à qui appartient telle
ou telle donnée sans se préocupper dans les détails de tout ce qui
est affectation et libération de mémoire (un simple delete sur un
objet vire toute la mémoire associée à cet objet). L'objet permet
aussi de ne jamais se poser les questions qui fâchent en utilisant
des fonctions toutes faites de la libcstd++. Au final, on développe
vite, mais le résultat est inefficace et consommateur de ressources.
La formation du développeur de base est aussi plus rapide (pas
besoin d'intégrer les mécanismes de base des listes chaînées, des
arbres et de toutes les joyeusetés de la programmation).
>>>> Que l'on voit très rapidement avec un schéma UML ce qui se passe
>>>> dans un code C++, c'est un fait.
>>> UML est indépendant du langage employé en phase de codage. Il y a des
>>> logiciels de conception UML qui savent générer du SQL, par exemple.
>>
>> Je parle d'objet, pas de SQL.
>
> Je voulais te montrer que UML n'est pas forcément lié au langage ni même
> aux langages objet puisque certains logiciels de conception UML savent
> générer du SQL qui n'est ni un langage objet, ni même un langage Turing
> complet (i.e. on ne sait pas exprimer tous les algorithmes existants
> dans ce langage : balayage récursif d'arbre, par exemple et c'est pour
> cela, entre autre, qu'existe le PL au-dessus de SQL).
Où ai-je prétendu le contraire ?
>>>> Que l'on soit capable inteligemmentde rajouter quelque chose dans le même programme est beaucoup plus
>>>> dur parce qu'il est dans les faits très compliqué de voir qui fait
>>>> quoi (mécanisme d'héritage, fonctions virtuelles, polymorphisme,
>>>> toussa...).
>>> Et bien non, c'est facile si l'application est bien conçue. Le
>>> polymorphisme ne s'applique qu'à une hiérarchie de classes, pas entre
>>> des classes de hiérarchies différentes.
>>> Le plus compliqué à comprendre est plutôt la programmation par
>>> délégation (en C++) car il faut passer par la surcharge de l'opérateur
>>> -> pour la faire "intelligemment".
>>>
>>> _Remarque_ : il semble, sans vouloir vous froisser, que vous n'ayez pas
>>> complètement assimilé l'approche objet dans sa globalité.
>>
>> Je pense au contraire que si. J'ai très bien assimilé l'approche
>> objet (et l'approche procédurale) et c'est bien pour cela que je me
>> permets de critiquer l'approche objet. Par ailleurs, si on pouvait
>> éviter le C++, parce qu'un langage où il faut mettre les
>> constructeurs de copie en purement virtuel pour éviter qu'il fasse
>> des tas de trucs dans mon dos n'est _pas_ un bon langage.
>
> C'est bien ce que je disais, tu ne maîtrises pas totalement ni
> l'approche objet ni le C++ ou tu suis aveuglément les préceptes des
> soi-disant "gourous" qui parlent de classe canonique, etc.sans avoir
> jamais réellement trempé dans de gros projets objets.
Si tu le dis...
> Les classes qui doivent posséder des constructeurs de copie (et aussi la
> surcharge de l'affectation) sont celles dans lesquelles il y a de
> l'allocation dynamique de mémoire dans les constructeurs.
> De plus un constructeur (en C++) ne peut jamais être virtuel (pur ou pas
> d'ailleurs). Peut-être voulais-tu dire destructeur car, oui, il faut des
> destructeurs virtuels dans ce cas pour libérer en cascade les objets
> alloués dynamiquement et faire agir le polymorphisme à la destruction
> des objets. Rien que de plus normal dans un cadre objet tout à fait
> ordinaire.
Je ne parle pas de _constructeur_, mais de _constructeur de copie_.
Je sais parfaitement qu'un constructeur ne peut pas être virtuel et
qu'il est de bon ton que le destructeur le soit.
> Le problème des constructeurs de copie c'est qu'ils sont appelés à
> chaque fois qu'on crée une copie d'une instance de la classe à laquelle
> ils appartiennent (en particulier quand on passe des instances de classe
> par valeur). Le moyen d'éviter cela est de passer les instances de
> classe par référence constante partout où l'on passe par valeur. Le
> passage par référence constante devrait être le mode de passage par
> défaut, en C++. Hélas, pour des raisons de compatibilité avec le langage
> C, il a été décidé de conserver le passage par valeur par défaut.
C'est encore une pierre dans le jardin du C++. Merci d'ajouter de
l'eau à mon moulin.