Arrêtez d’écrire des classes !

En créant les nouvelles fonctionnalités ASCII art de txt2tags, j’ai été amené à développer un certain nombre de fonctions spécifiques que j’ai donc logiquement fini par extraire dans une librairie autonome nommée aa.py. Pour des raisons historiques, d’habitude des utilisateurs et de facilité de diffusion, Aurélio Jargas – le BDFL de txt2tags – m’a demandé de réintégrer ces fonctions dans le fichier principal, ce à quoi il a ajouté :

Aurélio: move them back to global functions or creating a aa class in txt2tags?
option 2 would be cool

J’ai réfléchi, puis j’ai réintégré mes fonctions, telles quelles, sans créer de classe aa. Créer une classe dans ce cas ne me semblait pas adapté, complexifiant le code sans raison théorique valable ni contrepartie pratique positive.

Peu de temps après, je suis tombé sur une présentation intitulée Stop Writing Classes faite par le core developer Python Jack Diederich lors du PyCon 2012. J’y ai retrouvé beaucoup de choses que je pensais et d’autres que je n’avais pas encore clairement formalisées. Elle est particulièrement intéressante car elle utilise de vrais exemples de code, dont une horreur produite par des ingénieurs de chez Google !

On pourrait résumer cette réflexion avec les deux principes les plus importants de la programmation selon moi : KISS (Keep It Simple, Stupid) et DRY (Don’t Repeat Yourself). On peut remarquer que ce qui est pertinemment décrit comme une mauvaise utilisation des classes, correspond globalement à un style de programmation à la Java. Pour ceux qui ne comprendraient pas l’anglais et pour les gens pressés, voici donc les principales recommandations à retenir :

  • Diminuer le nombre de classes en refactorisant le code ;
  • Ne pas écrire de classes vides, qui pourraient être utiles plus tard, en utilisant pass, les écrire plus tard si besoin ;
  • Si une classe n’a que deux méthodes, et que l’une d’elle est __init__, c’est que ce ne devrait pas être une classe ;
  • Quand un dictionnaire suffit, ne pas le camoufler dans une classe ;
  • Ne pas créer de nouvelles exceptions, utiliser celles de la librairie standard, qui sont connues et comprises de tous ;
  • Les espaces de noms existent pour éviter les collisions, pas pour faire de la taxinomie, il faut donc qu’ils restent le plus plat possible.

Je finis en donnant la parole à la défense, avec cet article technique d’Armin Ronacher intitulé Start Writing More Classes.

Tags: , ,

7 Responses to “Arrêtez d’écrire des classes !”

  1. CarlChenet says:

    Réflexion très intéressante. La vidéo semble intéressante, je regarde ça dès que je peux.

  2. ctxnop says:

    Bonjour,
    Ce genre d’article me titille toujours… Ils sont toujours beaucoup trop “binaires”. “Toujours/Jamais”.
    Déjà je trouve que tu aurais du faire apparaitre “Python” dans le titre de ton article. Si on ne le lit pas en entier en plus de regarder la vidéo, tout en ayant assez de background sur d’autres langages, on pourrait croire que ce sont des choses à appliquer de manière générales. Or ce n’est absolument pas le cas. Ex: en C# on ne peut pas faire autrement que de mettre les fonctions dans des classes, en C++ la création d’exception personnalisée est encouragée, etc…

    Ensuite, même si le titre de la conférence est bien “stop writing classes”, il n’est pas aussi catégorique dans son développement. Il dit qu’il ne faut pas créer de classes si elles ne sont pas utiles. C’est un conseil générique qui s’applique à tout et n’importe quoi. On pourrait dire de ne pas compresser les données envoyées sur le réseau si ça n’est pas utile. En effet, si j’envoie 32 octets toutes les 20 secondes, ça ne sert absolument à rien de compresser. Déjà parce que je vais probablement avoir un résultat plus gros que les données d’origines, et que je consomme des ressources CPU pour quelque chose qui passerait quasi instantanément sur un 56k.

    Cependant, certains détails sont souvent oubliés dans ce type de conférence, d’ailleurs une personne dans la salle lui pose une question dans ce sens, ce à quoi il répond que “oui, il y a un risque d’appliquer trop à la lettre”.
    On oublie souvent beaucoup de questions sur le code qu’on tape. La première est “qui est le publique de mon code ?”. Est-ce que j’écris une application finale qui sera utilisée par un utilisateur lambda, qui ne verra jamais mon code ? Ou est-ce que l’utilisateur de mon code sera un développeur ?

    Je ne sais pas ce qu’il en est en Python, mais il y beaucoup de facteurs qui influencent la conception.
    C’est une lib et elle va évoluer, on peut étendre ce code à coup de plugin, il me faudra un binding vers tel ou tel autre langage, etc… Du coup on décider d’écrire une classe “vide” (ou ne contenant qu’une méthode) uniquement pour permettre l’héritage (qu’on ne fera pas forcément sois-même). On peut faire de l’héritage uniquement pour donner de la sémantique aux classes, même si ça n’est pas directement utile au code et même si Sean Parent à donné une conférence aux Going Natives nommée “Inheritance Is The Base Class of Evil”

    Bref tout ça pour dire que c’est la même histoire à chaque fois. Quelqu’un donne une conférence diabolisant une pratique qui l’insupporte (à juste titre ou non), fournit de très bon arguments pour l’exemple qu’il expose. Il titre avec quelque chose d’accrocheur et donc un minimum trollesque histoire d’interpeller, mais est beaucoup moins catégorique dans son développement. Mais cette personne le fait en parlant d’un contexte précis, ou en étant dans un contexte précis, comme une conférence sur un langage précis.
    Ensuite on voit fleurir des articles sur le net qui ne reprennent pas le contexte (ici : les conseils ne sont valables qu’en Python), sont complètement binaires (“Toujours/Jamais”), etc…

    Un petit bonus est qu’on nous sort à chaque fois le “KISS”/”DRY”. Ce sont de bon principes généraux, oui, mais ca reste des généralité. Si on appliquait le KISS réellement par exemple, on pourrait dire adieu aux algorithmes génétiques, aux réseaux de neurones, les hashs, les chiffrements, les compressions, ou même au type “std::string” du C++ (sérieusement, vous avez déjà regardé à quoi il ressemble ?! Si oui, vous ne pouvez pas dire que c’est simple… Et pourtant ce n’est qu’une foutue chaine… Un aperçu d’une des implémentation existante : http://www.sgi.com/tech/stl/string).
    La encore il faut relativiser le KISS. Déjà tout le monde ne comprends pas facilement les même choses. Pour certains un algo est simple, pour d’autre c’est un casse-tête insoluble.
    Doit-on éviter un algo performant, efficace, qui répond au besoin, etc… juste parce qu’il n’est pas accessible à tous les développeurs ? Je ne pense pas. Pour moi, entre deux solution qui font exactement la même chose, avec les mêmes avantages/inconvénients, il faut choisir celle perçue comme la plus simple pour tout le monde. Mais il ne faut pas aveuglément choisir une solution plus simple qu’une autre alors que cette autre solution apporte des choses qui peuvent être utiles. Préférer une solution un peu plus complexe mais qui permettra des évolutions, même si on pense qu’on aura jamais besoin de ces évolutions, est souvent, d’après mon expérience, une bien meilleure idée.

    Bref, tu l’auras compris, ces recommandations binaires m’exaspèrent, un peu comme tous ces articles que l’on voit indiquant que “On peut faire tel ou tel truc sur Ubuntu !” alors qu’ils parlent de quelque chose qui concerne tous les GNU/Linux, voir parfois tous les Unix.

    PS: Ne le prends pas pour toi, ma critique est générale. Ton article n’est qu’une toute petite goutte d’eau qui a fait débordé mon vase. Combiné au fait qu’on est lundi matin, qu’en plus on est le 30 décembre et que je suis au taf, du coup je m’emporte quelque peu. Un besoin d’évacuer quoi…

    • fgallaire says:

      Hello, merci de ton long commentaire argumenté, je regrette de ne pas en avoir plus de ce genre !

      Concernant le titre, comme tu l’as relevé je me suis contenté d’une traduction littérale de la présentation de Jack. Comme je suis un programmeur Python, j’ai l’habitude de préfixer le titre des articles par “Python :” ou de les suffixer par “en Python”, mais après réflexion j’ai préféré ne pas le faire ici, car je pense que cette réflexion dépasse largement le langage Python.

      Ainsi je suis d’accord avec toi : peut-on écrire moins de classes en Eiffel ? Non. Mais le raisonnement s’applique de mon point de vue : et si Eiffel était mal conçu ?

      Concernant le KISS je suis plus radical que toi : si c’est plus complexe, même si ça en fait plus, ça ne vaut sûrement pas le coup.

  3. haleth says:

    KISS ne veut pas dire “algo simpliste”.
    Il n’est pas question d’algorithme, mais d’implémentation : il faut implémenter les choses simplement (et faire des choses complexes, avec des briques simples).

  4. ctxnop says:

    Je dois avouer que je ne comptais pas faire si long, c’est une fois posté que j’ai vu le pavé… ^^’
    Bref, personnellement je trouve qu’au contraire ça ne s’applique pas vraiment aux autres langages. Bon évidemment je ne peut pas parler de tous les langages…

    Mais si je reprend les principaux : C#/VB.Net, Java => On ne peut pas faire autrement que de faire des classes. C => Il n’y a pas de classes. C++ => mon sentiment personnel est qu’il n’y a pas assez de classes. Beaucoup de dev viennent du C et on du mal a sortir la tête du procédural.

    Après c’est sur, d’autres langages existent et ca doit pouvoir s’appliquer à ceux-ci, mais bon…

    Ensuite, concernant le KISS je ne suis pas vraiment d’accord avec vous. Un algorithme est la description précise de la méthode à utiliser pour obtenir un résultat. L’implémentation dépend directement de l’algorithme. Modulo, évidemment, les subtilités du langage d’implémentation. C’est bien l’algorithme de compression qui fait que dans l’implémentation je vais utiliser des décalages binaires, des masques, des découpages en bloc, etc…

    Alors bien sur, le but est de faire des briques le plus simple possible pour pouvoir construire quelque chose de plus complexe. Mais a chercher trop simple on provoque la complexité et/ou la non-ré-utilisabilité.

    Mais surtout ce que je voulais dire à propos du KISS c’est qu’on le sort trop facilement en tant qu’étendard, un peu comme un point Godwin.
    Le KISS intervient quand un stagiaire écris une chaine séparée par des ‘;’ pour la spliter ensuite et évaluer, partie par partie la valeur en faisant des conversion vers int/double/… au lieu d’utiliser une structure avec les membres correctement typés, nommé, etc…
    C’est ici qu’on met une claque derrière la tête du stagiaire en lui disant “hey ! Keep it simple !”
    Le KISS ne dit pas qu’il ne faut pas faire quelque chose qui peut évoluer, quelque chose de plus complexe, etc…

    Dès que quelqu’un comprend mal quelque chose ou ne voit pas l’intérêt, il va se plaindre que c’est trop compliqué. Pas que c’est trop compliqué pour lui, mais bien trop compliqué tout court, de façon générale. Et si tu tente d’en discuter avec lui il va te dire “keep it simple”… A partir de la il n’y a plus de discussion, c’est fini (d’où ma comparaison avec le point Godwin). Soit tu lui impose ce choix et tu peux être sur qu’il ne l’utilisera pas, il créera son petit bout de code dans son coin ce qui apportera tout un tas d’emmerdements. Soit tu lâche l’affaire et tu fais quelque chose de plus simple, mais moins évoluer, ce qui apportera un tas d’emmerdements quand le temps sera venu de faire évoluer les choses (et ca c’est l’expérience du travail en équipe qui parle).

    Toutes ces méthodes, recommandations, etc… (KISS, DRY, Agile, Scrum, etc…) c’est super chouette sur le papier. Mais chacun y va se sa petite interprétation et concrètement, dans la vraie vie, ca tiens plus du far west que d’autre chose. Entre ceux qui en ont rien a foutre, ceux qui ne veulent pas changer leurs habitudes, ceux qui ne croient pas aux tests unitaires (si si, j’en connais un qui refuse catégoriquement qu’on en fasse parce qu’il est persuadé que ça n’apporte rien d’autres que du taff inutile) et j’en passe…
    C’est pour ça que je dis que le KISS ne doit pas être sorti a tout bout de champ car il n’est pas bien interprété par tout le monde et personne n’en a la même définition.
    La réalité c’est que le KISS dit “simple”, pas “simpliste”. Il faut faire simple, mais complet, capable de suivre une évolution, il doit également être simple à modifier, corriger, évoluer. S’il est compliqué les autres devs ne l’utiliseront pas, ou mal, même si tu documentes. S’il est simpliste, les autres devs vont créer d’autres systèmes plus adaptés a leurs besoin utilisant d’autres techno ou d’autres méthodes… Et c’est comme ça que tu te retrouve dans un projet avec 5 méthodes d’accès aux données en base… Prises unitairement chacune des méthodes suis le KISS : elle répond simplement au besoin. Mais 5 méthodes au lieu d’une seule, ce n’est pas simple. La maintenance est une horreur. Et si je voulais refactoriser tout ça pour n’avoir qu’une seule méthode a nouveau, le boulot serait colossale et apporterait énormément d’ennuis. Et de toute façon ça ne servirai pas a grand chose, il y aura toujours un con pour ne pas être satisfait et ajouter sa méthode…

    Bon, j’ai encore écris un sacré pavé… Je devrais peut être écrire sur mon blog qui est a l’abandon depuis plusieurs années plutôt que d’écrire des commentaires longs comme des articles sur ceux des autres…

    • fgallaire says:

      Oui clairement t’as des choses à dire et l’envie de la faire, tu serais plus à l’aise pour t’exprimer dans un vrai article de blog intitulé “KISS, Keep It Simple, not Simplistic, Stupid !” qu’en simples commentaires ici ! Merci cependant de tes contributions :-)

Leave a Reply