Procrastination

Cette semaine, suite à mon article précédent, j’avais prévu de parler d’éthique. Un sujet pas forcément facile, mais néanmoins indispensable. Et puis … les choses ne se sont pas exactement passées comme prévu.

J’ai d’abord passé beaucoup trop de temps à jouer à mindustry.

Et j’ai ensuite fait le choix douloureux d’un point de vue intellectuel, mais indispensable d’un point de vue culturel, de regarder les trois chef-d’œuvres du Marvel cinematic universe que sont Thor Ragnarok, Avengers l’ère d’ultron, et Captain America civil war. Au-delà des qualités artistiques de ces spectacles, il y a quand même des éléments intéressants pour cette réflexion éthique… mais pas de la manière à laquelle on penserait immédiatement. parce que si parfois ces personnage se pose des questions de l’ordre de la responsabilité, elles sont trop souvent évacuées par des ficelles scénaristique aussi épaisses que les bras de Captain America. Ce qui est en revanche bien plus intéressant, c’est ce qui est considéré comme faisant consensus. Et qu’est-ce qu’il fait consensus dans ses films ? C’est l’esthétique de la force du pouvoir, autrement dit l’esthétique fasciste.

Et ce que je crois, c’est que cette philosophie du pouvoir qui fait désormais partie du substrat culturel des Américains, et par leur impérialisme culturel de toute la partie occidentale de notre monde, contamine la vision que peuvent avoir du monde les entreprises à succès que sont les GAFAM. Je pense qu’il faut voir dans l’absence de réflexion éthique chez les informaticiens une conséquence de cette influence culturelle des Super Héros. En effet, et en particulier dans cette vision sans profondeur que peut-être le cinéma, il ne peux exister de contre-pouvoir. Autrement dit il ne peut exister de questionnement de la force, il ne peut pas y avoir de réflexion sur l’intérêt ou pas de son usage. La force se justifie par elle -même. Je trouve cette idée aussi terrible que terrifiante. Mais je ne peux pas m’empêcher d’y trouver une forme de justesse.

Informe éthique

Oui, je tente le jeu de mots faciles, précisément parce que le sujet est grave.

Grave à quel point ?

Avant de me lancer dans un catalogue d’exemple navrant, je vais commencer par où donner une perspective historique audiophile. Si comme moi vous écoutez les castcodeurs, vous savez que lorsque ce podcast a commencé, il n’existait pas vraiment de partie loi société et organisation. Et aujourd’hui, en 2021, cette partie nécessite des épisodes spéciaux.

Pourquoi ?

Sans doute parce que comme le dit mon PDG, « Software is eating the world », et que le logiciel ne choisit pas forcément les meilleurs morceaux.

Je vais vous donner quelques exemples qui m’ont marqué dans les dernières années.

Le premier, qui traite évidemment de la responsabilité des informaticiens, et celui de la débâcle de Volkswagen concernant ses émissions carbone un développeur a été condamné, mais certainement pas le donneur d’ordre. Et pendant ce temps-là aux États-Unis, les libérations conditionnelles sont selon les états approuvées par un algorithme tout aussi raciste que le système qui l’a implémenté. Et si vous croyez que cet exemple ne passe pas les frontières françaises, je pense qu’il suffira de faire un tour dans le domaine de la banque et de l’assurance pour constater que le scoring algorithmique est une réalité qui s’impose, et qui s’impose d’autant plus qu’on a plus besoin de crédit, de facilités de paiement, de retard d’agios, bref, qu’on est dans la merde. En terme de contenu proposé, il vous suffit de faire un tour sur n’importe quel réseau social pour comprendre que le concept même de flux d’information algorithmique est soumis à un ensemble de biais qui sont aussi connus que dévastateur pour la démocratie, l’égalité des races et des sexes, l’égalité des chances. Et malheureusement, je ne résiste pas à l’envie de vous faire remarquer que ces algorithmes sont corrigés lors du changement de législature.

Que faire ?

Pour l’instant, je ne vais pas forcément réfléchir à des solutions, même si j’en ai quelques-unes en tête, j’essaie simplement de bien vous faire comprendre que le contexte économique de réalisation des logiciels favorise le conservatisme, les biais d’opacité, la conservation des structures de pouvoir.

Focus forcé

j’ai lu hier le livre Focus qui traite de productivité personnelle. Le postulat de ce livre est aussi simple que facile à résumer :  » En cet âge de distraction, concentrez-vous sur une tâche à la fois et vous arriverez ». Il fait écho aux travaux de Cal Newport dont le dernier article sur Beethoven est également une bonne lecture.

Et en vérité je me retrouve assez dans ce qu’ils écrivent. En effet depuis mon lit de convalescence, je n’ai accès qu’à une tablette dépassée et un téléphone portable. Que me reste-t-il dans ce contexte si ce n’est mon imagination et ma capacité à réfléchir ? Cela explique sans doute que j’ai produit un article par jour alors que ce blog était quasiment mort 😃. Au moins un autre point à noter c’est que aussi bien cette tablette que ce téléphone ne permettre que d’avoir des applications en plein écran. Il est particulièrement fastidieux avec Android de jongler d’une application à une autre. La concentration devient facilité par les outils informatiques à disposition. Et c’est quelque part une autre leçon évidemment bien connu, qui est que le format conditionne l’ œuvre. Et quelque part c’est en des secrets cachés dans ses outils de développement personnel qui est que selon ce qu’on veut produire il vaut mieux utiliser un ordinateur une tablette ou tout simplement réfléchir et marcher.

Android et obsolesence

Comme je le disais hier, je suis coincé dans mon lit. Heureusement je m’étais préparé avant cette opération à la phase de convalescence qui devait se faire couché. je vais donc récupérer une tablette qui traînait à la maison sur laquelle j’ai installé les différentes applications utiles. Enfin, j’ai essayé d’installer ces applications. En effet, même si Android et bien plus récent que Windows, l’obsolescence frappe l’écosystème logiciel. Pour donner quelques exemples, swype keyboard, Firefox, cosy Drive, ne sont pas disponibles sous Android Kit-Kat. plus exactement ils ne sont plus disponibles pour cette version parce que je me souviens très bien avoir installé swipe keyboard sur mon premier téléphone Wiko il y a désormais quelques années. Alors, pourquoi ces applications ne sont-elles plus disponibles ? Est-ce que la Fondation Mozilla ne livre Firefox que pour les version récente d’Android ? Est-ce que Microsoft ne livre swipe keyboard que pour les dernières versions ? où est la salle de l’illustration valable de la balkanisation du marché des distributions Android chez les fabricants de téléphones ? J’ai tendance à penser que la dernière question est la bonne puisque plutôt que de désigner un unique coupable dans une espèce de théorie du complot un peu facile elle mais plutôt en avant les différents intérêts politiques qui secoue cet écosystème. En effet Google a beau produire le système d’exploitation, celui-ci est installé dans les matériels par les fabricants qui rajoute des surcouche du bloatware et qui choisissent sans doute (mais je ne sais pas) la politique de mise à jour. Cela dit ce voyage dans le passé d’Android a quelques avantages d’abord il me permet de tester AnySoftKeyboard qui permet beaucoup plus facilement à mon goût que swipe keyboard de tester de nouvelles dispositions clavier ( par exemple le Bepo). Par ailleurs il me permet également de tester comme pour cet article la saisie vocale de d’Android. et je dois dire qu’une fois qu’on met de côté les contraintes de protection des données personnelles liées au fait que la reconnaissance vocale soit effectuée sur les serveurs de Google ( ce qui n’est pas une question sur laquelle je m’assieds facilement), il est très agréable de dicter son texte et de le voir apparaître l’écran. C’est une manière beaucoup plus fluide de communiquer avec son lectorat même si je crains que ça ne donne à ce texte une tournure plus orale et puis ça me rappelle Terry Pratchett qui, dans la préface de son dernier livre « je m’habillerai de nuit » explique justement avoir dû passer à cette technique pour d’autres raisons, bien plus graves, que les miennes.

Extrême confinement

Non je ne vais pas vous parler du coronavirus.Je vais plutôt vous parler de moi et de mon dos. En effet je souffre depuis quelques mois d’une hernie discale lombaire. Et si vous ne connaissez pas cette pathologie, c’est à la fois douloureux etune illustration parfaite d’un biais de confirmation. En effet on a tendance à penser que l’être humain est le sommet de l’évolution, le pinacle de 4 milliards d’années de transformation, or en fait il s’agirait plutôt d’un bricolage construit exactement comme les systèmes informatiques et les villes c’est-à-dire sur les ruines de l’organisme précédent. En l’occurrence la colonne vertébrale station une illustration éclatante (je pourrais parler aussi évidemment du coccyx, des dents de sagesse et d’autres mais la colonne vertébrale m’intéresse plus pour des raisons assez évidentes) si vous la regardez vous avez l’impression que c’est un truc raisonnablement bien conçu sauf que un moment donné vous voyez que du côté des vertèbres lombaires la colonne et à peu près aussi droite que la tour de Pise et ça ne s’arrange pas quand on remonte puisque au niveau des épaules elle part en arrière avant de repartir en avant. Franchement ça tient à peine debout et c’est normal parce que de toute façon ça n’est pas fait pour ça. L’immense majorité des animaux la met en effet de façon horizontale par rapport au sol sans doute une question de répartition des masses ou d’autres sujets que je ne vais pas forcément creuser ici. Toujours est-il que la nôtre tient debout par hasard plus que par souci d’une conception élaboré. Parce qu’anatomiquement, c’est le Mega burger de la mort vous avez un empilement de parties dures (les vertèbres) et de parties molles (les disques) et ces disques peuvent se rompre ouplutôt voir leur contenu se répandre (d’où douleurs variées et impotences désagréables quand cette mayonnaise vient frotter vos nerfs) c’est ce qui fait que depuis hier et pendant un mois je vais avoir droit au plus extrême des confinements. En effet je suis dans ma chambre avec pour toute vue une fenêtre qui donne sur les nuages c’est en quelque sorte un voyage intérieur qui commence.

lifestream et dépendances

Il y a quelques temps, j’avais entamé un projet de lifestream, un outil qui me permettrait de rassembler au même endroit l’ensemble de mes écrits quelquesoit (ou à peu près) le support. Après quelques alternoiements, j’ai enfin une version qui marche, et j’en suis plutôt content (même si la synchronisation depuis ma machine vers les serveurs web de free.fr est encore un peu capricieuse). Je retiens de ce projet plusieurs choses.

D’abord, un peu comme sur ma mission actuelle, faire les choses simplement, c’est mieux, surtout lorsque le mainteneur reprend le code après une pause de deux ou trois ans.

Ensuite, certains choix de dépendances peuvent faire mal. Et c’est drôle parce que ça me rappelle cette discussion récente

En effet, pendant longtemps pour mon lifestream, je n’utilisais volontairement que HtmlUnit comme client web. Et pour Shaarli, comme pour Goodreads, ça marchait très bien. En revanche, pour WordPress.com, je n’ai jamais réussi à passer la page de login. Elle a pourtant l’air simple, cette page, non ? Eh bien en fait, c’est une horreur sans nom qui ne fonctionne qu’avec le Javascript, et pire qu’avec « simplement » du Javascript, il vous faut le vrai environnement d’exécution d’un navigateur, et pas ce que propose HtmlUnit. De ce fait, j’ai dû changer mon fusil d’épaule et remplacer le modeste HtmlUnit par la lourdeur de Selenium (qui pilote dans ce cas Firefox). Et on voit revenir le débat que ma question avait levé, dans une version différente.

Parce que clairement, personne ne se voit coder en une journée un client web. En revanche, en choisir un qui colle bien à mes besoins est assez facile (même si en l’espèce passer à Selenium implique que la « machine » qui fait tourner mon lifestream doit disposer d’un vrai navigateur, ce qui est une sacrée contrainte). Alors comment je peux faire le lien entre ce gros bloc fonctionnel et la petite dépendance que peut être la définition d’un composant de programmation fonctionnelle comme Either ?

Parce que c’est bien ce que me reprochent Nicolas et Loïc, non ?

Oui, c’est bien ce qu’ils me reprochent, ne pas coder Either moi-même

Eh bien dans ce cas, qui est en fait le même que celui du navigateur web, la vraie question est : est-ce que je veux entrer dans le détail de cette abstraction parce que je prétends la comprendre mieux que les développeurs initiaux ? Dans le cas précis d’Either, je pourrais utiliser la défense classique : pourquoi utilisez-vous le JDK plutôt que de tout recoder depuis objet ? Mais en vérité, ça n’est pas la bonne défense. La bonne défense, c’est que pour moi, Either (d’autant plus après la lecture d’un très bon article introductif sur les monades) est une des briques de base de la programmation fonctionnelle, et que je peux difficilement me permettre de recoder cette brique de base si je n’ai pas compris l’ensemble des détails afférents. Et clairement, je n’ai pas les connaissances suffisantes pour ce faire.

Et la vérité, c’est que pour moi, l’une des raisons fondamentales à l’usage de dépendances, c’est d’utiliser des abstractions sur étagères plutôt que de les recréer moi-même. J’en veux pour preuve un autre exemple, dans ce même projet de lifestream. Dans ce projet, j’utilise le très pratique commons-vfs pour lire et écrire dans les systèmes de fichiers (ça simplifie bien la vie). Et j’ai récemment ajouté la lecture des fichiers dans le zip fourni par l’export de WordPress. Or l’implémentation standard de commons-vfs n’est pas capable de lister le contenu d’un zip. Mais, comme j’ai déjà beaucoup utilisé cette librairie, je connais bien cette limitation … et j’ai même écrit une librairie complémentaire : commons-vfs-truezip. J’ai donc utilisé cette extension à cette librairie pour supporter mon cas d’utilisation, sans perdre les fonctionnalités que m’apporte commons-vfs. Et je crois que ça montre bien quelle est ma méthode : utiliser les dépendances qui ne correspondent pas au projet que je développe, même si j’ai moi-même créé ces dépendances, plutôt que de faire de mon code une solution à des tonnes de problèmes différents.

On verra ce que cette méthode donnera pour mon prochain « gros » projet …

Un phishing bien malin

Je ne parle d’habitude pas des tentatives d’individus pas si bien intentionnés de pénétrer dans ma boîte mail.

Mais là, mon compte free reçoit maintenant des messages finement forgés. Laissez-moi vous montrer un exemple de message …

Mon adresse mail n’est évidement pas tontonjohnny@free.fr … qui est néanmoins une adresse free.fr parfaitement valide. Tout comme ebp.com d’ailleurs. Les pièces jointes sont évidement de vraies factures émises par EBP pour un de ses clients qui existe réellement. Mais regardez bien l’url qui s’affiche en bas quand je mets ma souris sur « Accéder à ma facture ». Ca n’est certainement pas une url chez ebp. Donc c’est un faux. Et en cliquant sur ce lien, on arrive finalement sur de bonnes pages bien crapuleuses : installation d’antivirus à la manque, participation à des tirages au sort bidon, bref, tout y passe. Alors comment ces messages arrivent-ils chez moi ?

C’est le moment de jeter un oeil aux entêtes

Return-Path: info.fr@ebp.com
Received: from zimbra43-e7.priv.proxad.net (LHLO
 zimbra43-e7.priv.proxad.net) (172.20.243.193) by
 zimbra43-e7.priv.proxad.net with LMTP; Fri, 8 Jan 2021 15:22:01 +0100 (CET)
Received: from bookyoo.com (mx28-g26.priv.proxad.net [172.20.243.98])
	by zimbra43-e7.priv.proxad.net (Postfix) with ESMTP id 15FD553742D
	for <nicolas.delsaux@free.fr>; Fri,  8 Jan 2021 15:21:53 +0100 (CET)
Received: from bookyoo.com ([5.139.18.132])
	by mx1-g20.free.fr (MXproxy) for nicolas.delsaux@free.fr;
	Fri,  8 Jan 2021 15:22:01 +0100 (CET)
X-ProXaD-SC: state=HAM score=0
X-ProXaD-Cause: (null)
Content-Type: multipart/mixed;
 boundary=--boundary_7272_789646fc-a564-46c2-b8cd-0f7a6d6b092d
MIME-Version: 1.0
Reply-To: no-reply@ebp.com
Subject: =?utf-8?B?TWlzZSDDoCBkaXNwb3NpdGlvbiBkZSB2b3RyZSBmYWN0dXJl?=
 =?utf-8?B?IE7CsDAwOTE1MjAwMDI=?=
Message-ID: <491aded745ca4af31dc241278d594bdd@mwinf5c81.me-wanadoo.net>
Date: Fri, 08 Jan 2021 14:11:29 +0000
From: "EBP service Client" <info.fr@ebp.com>
To: tontonjohnny@free.fr


----boundary_7272_789646fc-a564-46c2-b8cd-0f7a6d6b092d
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Pas la peine de mettre le contenu du message, hein.

Donc, qu’est-ce qu’on observe ? D’abord, que le champ To: correspond bien à la mauvaise adresse. Personnellement, je pensais que free.fr vérifiait bien que ce To: correspondait bien à l’adresse mail. Du coup, il y a quelque chose qui m’échappe …

Et du coup, je suis très intrigué …

Surtout que les mails frauduleux ne sont évidement pas toujours des mails ressemblant à ceux d’EBP …

En revanche, si on regarde les champs Received:, le mail vient de Chine.

Mais on peut aller plus loin … regardez cette question sur superuser How can I find out where an email really came from?

Ca nous amène sur la MX Toolbox, qui semble indiquer des problèmes, mais je ne suis toujours pas sûr de comprendre comment ce mail a pu arriver dans ma boîte mail …

Et si … je relancais un nomic ?

Ca fait un moment que ça me titille. Et j’en ai même fait une présentation pour quelques collègues.

Et vu que certains y avaient trouvé de l’intérêt, je vais lancer quelque chose de simple histoire de voir où ça pourrait me mener (avant une initiative bien plus ambitieuse à base de GitHub et de pull requests, voire même de Catala). Bref, allons-y pour quelques règles simples. Et je vais noter les règles avec mes différents commentaires à la fin

  1. ZeNomic est une partie de nomic se jouant sur Workplace
  2. Le texte d’une règle ou d’une proposition ne doit pas dépasser 50 mots.
  3. Une proposition peut être un ajout, une modification, ou une suppression de règle.
  4. Chaque proposition de règle doit être faite dans via un sondage dans lequel les réponses valides sont « oui » ou « non ».
  5. Un sondage en vue de valider une proposition doit être ouvert au moins 12 heures, et au plus 27 jours.
  6. Une proposition est acceptée si plus de la moitié des réponses au sondage (les commentaires ne comptent pas) sont « oui » au moment de la clôture du sondage.
  7. Lorsqu’une proposition est acceptée, le joueur l’ayant proposée doit envoyer un message contenant toutes les règles en cours de validité.
  8. Chaque personne proposant une règle ou votant lors d’une proposition est un joueur pour le reste de la partie.
  9. A tout instant, il ne peut pas y avoir plus d’une proposition de règle en cours de vote. Les propositions supplémentaires sont ignorées.

Et maintenant, quelques commentaires

  1. Comme l’objectif est de jouer avec mes collègues, et que le réseau social d’entreprise est Workplace (même si ça ne me plaît pas du tout), autant essayer d’en faire quelque chose d’intéressant …
  2. Je suis depuis Conanomic accro aux limites de taille de règles. Et si la limite à une phrase avait du sens pour Conanomic, la limite la plus simple est celle du nombre de mots.
  3. J’ai failli oublier cet élément essentiel !
  4. Dès que j’ai posé les yeux sur Workplace, je me suis dit que je devais m’en servir pour un nomic. En particulier les sondages, qui sont vraiment faits pour voter.
  5. Je n’avais initialement pas écrit cette règle. Et puis je me suis rendu compte que sans ça, on pouvait bloquer la partie avec un sondage sans limite de validité, ou avancer librement en ouvrant des sondages très courts et en y répondant.
  6. Je compte sur la subtilité des joueurs pour changer ces conditions fort simples assez rapidement.
  7. Oui, bon, ça, c’est fastidieux, mais je pense qu’on doit pouvoir exploiter le média d’une façon rigolote.
  8. Et oui, il faut bien définir les joueurs !
  9. Et on termine par la limitation classique pour limiter l’activité, sans ça, ça diverge rapidement.

Et onv a maintenant tester tout ça !

Bonne année !

Est-il utile de rappeler que 2020 a été une année .. particulière ?

Elle avait pourtant bien commencé, avec un super snowcamp et cette présentation (pour laquelle je suis toujours stupéfait de constater qu’il y a eu une bonne centaine de personnes intéressées).

Je constate d’ailleurs avec un dégoût à peine voilé que Jacques Séguéla a fait une conférence au titre assez proche … J’ai donc le même goût du calembour boiteux que cet inénarrable pubard ?

A titre personnelle, je peux dire sans rougir que 2020 aura été pour moi l’année du chômage partiel. Une découverte … dont j’aurais pu me passer. Cela dit, ça m’a permis de réfléchir à quelques sujets, comme l’architecture logicielle avec C4 (dont j’adorerais parler en conférence, j’ai même déja un support raisonnablement prêt). 2020, c’est aussi une année qui s’est terminée … dans la douleur. Et quelque part, sans doute parce que j’y serai cette année encore plus sensible, le fameux voeu de « et surtout la santé » me rend vraiment hargneux. Ca veut dire quoi « la santé » ? Et la santé sans le reste a-t-elle un sens ? Souhaiter la bonne santé, c’est avant tout souhaiter aux français marqués par cette stupidité de dichotomie corps-esprit que l’animal en eux ne s’impose pas, non ? Bon, je digresse.

L’aspect le plus navrant, c’est que la plupart de mes idées marrantes n’ont pas avancé d’un poil : la webradio est toujours en pièces, je n’ai pas fait beaucoup de Rust (pas autant que j’aurais aimé), bref, l’épidémie a pas mal pesé sur moi.

Alors ? Que faire de 2021 ? En fait, je suis assez indécis. Je commence réellement à penser qu’il est temps de changer quelques orientations … Et je vais me permettre une métaphore. Dans les milieux de la science-fiction littéraire, il y a une question qui hante : la science-fiction a-t-elle gagné ou perdu ? Autrement dit, le genre science-fictif a-t-il gagné ses lettres de noblesse ou reste-il un décor pour des oeuvres sans grand sens ? Cette question, je pense qu’on peut se la poser au sujet de l’informatique : aujourd’hui, plus aucune entreprise n’existe en-dehors de son informatique (oui, posé comme ça, c’est prétentieux, mais dans la mesure où l’informatique est le cerveau et le système nerveux des entreprises, sans cette informatique, la plupart des entreprises disparaissent immédiatement). Par ailleurs, les plus fortes valorisations sont aujourd’hui des entreprises du domaine informatique. Est-ce que ça traduit réellement un engouement pour ces technologies ? Non. Ca traduit uniquement le fait que l’espionnage commercial, renommé en intelligence économique et en connaissance client, est littéralement devenu le pétrole du XXIème siècle, c’est-à-dire un élément moteur de l’économie. Tout cela va sans doute faire émerger un business générant de la pollution de notre sphère informationnelle personnelle, et c’est déja le cas grâce à des « leaders » comme Google, bien sûr, mais aussi la fameuse licorne française Criteo … Et c’est là où je voulais en venir avec ma métaphore : l’informatique a effectivement conquis les entreprises comme un outil corruptif : chaque dirigeant est tenté d’informatiser plus son entreprise, de rajouter de « l’intelligence artificielle », des « algorithmes » dans une quête d’une rentabilité améliorée … Or je pense que cette rentabilité améliorée ne justifie la plupart du temps pas les dépenses qui sont engagées dans sa poursuite. Mais comment le montrer ? Et surtout, comment le montrer dans un monde économique qui continue à croire au mythe de la croissance éternelle ?

Parce que le corollaire de cette question, c’est évidement la question du but : pourquoi faire tous ces efforts ? Pourquoi continuer à chercher à produire plus dans un monde que les catastrophistes nous décrivent comme à l’agonie ? Personnellement, sans croire uniquement au solutionnisme technologique, je pense qu’on peut sauver la planète … pour peu qu’on réoriente toute cette intelligence actuellement occupée à « produire de la richesse » vers une intelligence plus verte, qui essaierait de préserver le monde qui nous entoure.

Et figurez-vous que c’est peut-être la meilleure justification pour reprendre mes efforts vers plus de Rust : produire des solutions logicielles plus efficaces énergétiquement. Et j’aimerais bien un jour creuser l’intérêt des méthodes DevOps vis-à-vis de l’intérêt écologique … Parce que ces histoires de recompilation de l’univers à chaque commit me paraissent de plus en plus difficiles à vivre ..

Mais tout ça, c’est le futur, qui n’attend que nous ! Alors allons-y !

Un visiteur … venu d’ailleurs

Ce titre d’un goût douteux vient d’une présentation du design pattern visiteur que j’ai fait il y a peu, et dont je me suis dit qu’il serait chouette d’en faire un article « propre ». Et comme la plupart des articles qui en parlent le font en UML, je vais vous le montrer avec du code … Java (parce que c’est ce que je pratique).

Supposons donc que je souhaite afficher le dessin d’un arbre (oui, c’est un peu abstrait, mais c’est la version la plus concrète que j’ai trouvé, grâce à mon collègue Delphin). Pour ça, vous disposez de quelques classes : Feuille, Fleur. Et comme ce code est du code déja existant, les méthodes d’affichage portent des noms différents … ce qui peut donner lieu par exemple à ce code (beaucoup trop simpliste, mais c’est pour donner l’idée).

import java.util.Arrays;
import java.util.List;
class Feuille {
public String montrerFeuille() { return "🍃"; }
}
class Fleur {
public String afficherFleur() { return "🌺"; }
}
public class Main {
public static void main(String[] args) {
List<Object> visitables = Arrays.asList(new Feuille(), new Fleur());
for (Object v : visitables) {
if (v instanceof Feuille) {
Feuille f = (Feuille) v;
System.out.println(f.montrerFeuille());
} else if (v instanceof Fleur) {
Fleur f = (Fleur) v;
System.out.println(f.afficherFleur());
}
}
}
}
view raw Main.java hosted with ❤ by GitHub

Bon, évidement, c’est un exemple pourri. Parce que ce que vous voulez faire, c’est les mettre dans une liste, et appeler leurs méthodes. Dans une vision classique, vous allez ajouter une interface, et ça donnera ce code

import java.util.Arrays;
import java.util.List;
class Feuille implements Affichable {
public String montrerFeuille() { return "🍃"; }
@Override public String afficher() { return montrerFeuille(); }
}
class Fleur implements Affichable {
public String afficherFleur() { return "🌺"; }
@Override public String afficher() { return afficherFleur(); }
}
interface Affichable {
public String afficher();
}
public class Main {
public static void main(String[] args) {
List<Affichable> affichables = Arrays.asList(new Feuille(), new Fleur());
for (Affichable affichable : affichables) {
System.out.println(affichable.afficher());
}
}
}
view raw Main.java hosted with ❤ by GitHub

Qui présente en fait un inconvénient important : à chaque usage, vous allez ajouter des méthodes dans votre interface, et les implémenter dans chacune de ces classes. Et le code de votre domaine se retrouvera pollué par des tonnes de bouts de trucs qui n’ont que peu de rapport. Or le principe de la programmation structurée, quelquesoit la structure, c’est de limiter la complexité, pour permettre au développeur d’attaquer des problèmes plus complexes. Et c’est à ça que sert le visiteur.

Alors comment faire ? Eh bien, grâce au visiteur, pardi ! Celui-ci repose sur deux idées. D’abord, rendre notre modèle visitable, comme un musée, c’est-à-dire créer une interface Visitable implémentée par les différents éléments du modèle. Ensuite, créer une interface Visitor et des implémentations qui vont isoler la complexité. Regardez, ça n’est pas si compliqué.

class Feuille implements Visitable {
public String montrerFeuille() { return "🍃"; }
@Override public void accept(Visitor v) { v.visit(this); }
}
class Fleur implements Visitable {
public String afficherFleur() { return "🌺"; }
@Override public void accept(Visitor v) { v.visit(this); }
}
interface Visitable {
public void accept(Visitor v);
}
interface Visitor {
void visit(Feuille feuille);
void visit(Fleur fleur);
}
public class Main {
public static void main(String[] args) {
List<Visitable> visitables = Arrays.asList(new Feuille(), new Fleur());
for (Visitable v : visitables) {
v.accept(new Visitor() {
@Override public void visit(Feuille feuille) {
System.out.println(feuille.montrerFeuille());
}
@Override public void visit(Fleur fleur) {
System.out.println(fleur.afficherFleur());
}
});
}
}
}
view raw Main.java hosted with ❤ by GitHub

Toutefois, il y a encore plusieurs problèmes.

Le premier est assez simple : nos objets sont beaucoup trop basiques. Et dans un modèle réel, il faudrait avoir des objets conteneurs. Dans notre cas, ce sera … la Branche.

class Feuille implements Visitable {
public String montrerFeuille() { return "🍃"; }
@Override public void accept(Visitor v) { v.visit(this); }
}
class Fleur implements Visitable {
public String afficherFleur() { return "🌺"; }
@Override public void accept(Visitor v) { v.visit(this); }
}
class Branche implements Visitable {
List<Visitable> children = new ArrayList<Visitable>();
public Branche(List<Visitable> children) { this.children.addAll(children); }
public Branche(Visitablechildren) { this(Arrays.asList(children)); }
@Override public void accept(Visitor v) {
v.startVisit(this);
for(Visitable c : children) {
c.accept(v);
}
v.endVisit(this);
}
}
interface Visitable {
public void accept(Visitor v);
}
interface Visitor {
void visit(Feuille feuille);
void visit(Fleur fleur);
void startVisit(Branche branche);
void endVisit(Branche branche);
}
public class Main {
public static void main(String[] args) {
List<Visitable> visitables = Arrays.asList(new Feuille(), new Fleur(),
new Branche(new Feuille(), new Fleur()));
for (Visitable v : visitables) {
v.accept(new Visitor() {
@Override public void visit(Feuille feuille) {
System.out.println(feuille.montrerFeuille());
}
@Override public void visit(Fleur fleur) {
System.out.println(fleur.afficherFleur());
}
@Override
public void startVisit(Branche branche) {
System.out.println("➡🌿");
}
@Override
public void endVisit(Branche branche) {
System.out.println("⬅🌿");
}
});
}
}
}
view raw Main.java hosted with ❤ by GitHub

Et comme celle-ci implémente visitable, on voit bien que le compilateur va nous aider à implémenter le bon code :

  1. On ajoute l’interface Visitable, du coup le compilateur nous dit qu’il faut rajouter la bonne méthode.
  2. Quand on a implémenté l’interface Visitable, on a écrit la ligne visitor.startVisit(this); et visitor.endVisit(this); qui n’existent pas dans l’interface Visitor. Il faut donc les ajouter.
  3. Et par conséquent, il faut aussi les ajouter dans l’implémentation anonyme qu’on utilise dans notre classe Main.

Et ça, c’est un gros avantage du visiteur sur les interfaces « ad-hoc » : chaque usage est facilement identifié par le compilateur (ce qui est encore plus performant que de l’identifier par la recherche de code d’un IDE quelconque. Et ça, c’est un avantage d’autant plus performant que la base de code est importante.

Cela dit, si vous regardez le code dans la classe Main, ça n’est pas très joli : il y a des tas de System.out.println, et personnellement, je préfèrerai en avoir un seul … Ca veut dire construire une chaîne de caractère dans le visiteur et la retourner. Bon, on parle de String, mais ce serait bien si on pouvait retourner n’importe quoi, non ? Heureusement, avec Java, on peut faire des types génériques. Regardez le code suivant

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class Feuille implements Visitable {
public String montrerFeuille() { return "🍃"; }
@Override public <Type> Type accept(Visitor<Type> v) { return v.visit(this); }
}
class Fleur implements Visitable {
public String afficherFleur() { return "🌺"; }
@Override public <Type> Type accept(Visitor<Type> v) { return v.visit(this); }
}
class Branche implements Visitable {
List<Visitable> children = new ArrayList<Visitable>();
public Branche(List<Visitable> children) { this.children.addAll(children); }
public Branche(Visitablechildren) { this(Arrays.asList(children)); }
@Override public <Type> Type accept(Visitor<Type> v) {
v.startVisit(this);
for(Visitable c : children) {
c.accept(v);
}
return v.endVisit(this);
}
}
interface Visitable {
public <Type> Type accept(Visitor<Type> v);
}
interface Visitor<Type> {
Type visit(Feuille feuille);
Type visit(Fleur fleur);
void startVisit(Branche branche);
Type endVisit(Branche branche);
}
public class Main {
public static void main(String[] args) {
List<Visitable> visitables = Arrays.asList(new Feuille(), new Fleur(),
new Branche(new Feuille(), new Fleur()));
for (Visitable v : visitables) {
System.out.println(v.accept(new Visitor<String>() {
@Override public String visit(Feuille feuille) {
return feuille.montrerFeuille();
}
@Override public String visit(Fleur fleur) {
return fleur.afficherFleur();
}
@Override public void startVisit(Branche branche) {
}
@Override public String endVisit(Branche branche) {
return "⬅🌿";
}
}));
}
}
}
view raw Main.java hosted with ❤ by GitHub

Et au passage, une petite astuce : quand le code commence à utiliser plusieurs classes (et c’est le cas ici), commencez par lire le code utilisateur. En l’occurrence, le code qui est dans la méthode main(...). Dans celui-ci, on voit bien que le visiteur est conçu pour ne retourner que des String, parce que j’utilise les types génériques.

Il y a toutefois un défaut. Dans ce cas, on n’a pas retourné le contenu de notre branche. Pour changer ça (et c’est précisément l’intérêt du visiteur), il suffit de modifier le visiteur défini dans la classe Main pour y ajouter un StringBuilder qu’on retourne … comme ça

new Visitor<String>() {
StringBuilder returned = new StringBuilder();
@Override public String visit(Feuille feuille) {
return returned.append(feuille.montrerFeuille()).append('\n').toString();
}
@Override public String visit(Fleur fleur) {
return returned.append(fleur.afficherFleur()).append('\n').toString();
}
@Override public void startVisit(Branche branche) {
returned.append("➡🌿").append('\n').toString();
}
@Override public String endVisit(Branche branche) {
return returned.append("⬅🌿").append('\n').toString();
}
}
view raw Visitor.java hosted with ❤ by GitHub

D’une manière générale, vous allez vite apprendre à aimer les Stack (quelquesoit leur forme) avec les visteurs 😅.

Il nous manque une dernière chose : quand je visite un musée, je ne visite pas forcément toutes les pièces. Alors, comment faire un visiteur qui ne passe pas dans tous les objets ? Simplement en modifiant notre méthode startVisit(...) (je ne mets que les morceaux intéressants) :

class Branche implements Visitable {
// [….]
@Override public <Type> Type accept(Visitor<Type> v) {
if(v.startVisit(this)) {
for(Visitable c : children) {
c.accept(v);
}
}
return v.endVisit(this);
}
}
interface Visitor<Type> {
// [….]
boolean startVisit(Branche branche);
// [….]
}
public class Main {
// [….]
@Override public boolean startVisit(Branche branche) {
returned.append("➡🌿").append('\n').toString();
return true;
}
// [….]
}
view raw Main.java hosted with ❤ by GitHub

Et là, vous avez l’implémentation « canonique » d’un visiteur en Java.

Ce design pattern est en fait très utilisé : dès que vous transformez un modèle en un autre, il est très pratique. Et ces transformations, vous en faites …. tout le temps : compter les objets, c’est une transformation, tout comme afficher un text, ou calculer une somme. Pour la petite histoire, le collègue qui me l’a fait découvrir il y a dix ans en mettait partout … et à juste titre. Par ailleurs, je l’ai implémenté ici en java, mais j’ai déjà eu des résultats satisfaisants dans d’autres langages : du Rust, du Python, du Groovy (évidement). A mon sens, tous les langages présentant des structures de données arborescentes peuvent y trouver de l’utilité.