La semaine dernière, je n'avais pas assez de temps pour développer ma pensée et revenir sur le fond de l'article du lien; aujourd'hui je le peux.
Je voudrais d'abord revenir sur un point qu'il rejette : l'encapsulation ou les types de données abstraits, résumés sous le principe "Code should not know about the internals of objects it’s working with". C'est là un principe que je ne renierai jamais, et dont les développeurs OCaml font un usage abondant : on s'en fout, la plupart du temps, de la représentation concrète d'un type, ce que l'on veut connaître c'est son interface ou API, c'est-à-dire ce que l'on peut faire avec.
J'ai toujours aimé la présentation qu'en faisait John C. Reynolds dans son article Types, abstraction and parametric polymorphism. Il présente l'idée sous la forme d'une fable où des étudiants d'université suivent des cours sur les nombres complexes. Certains étudiants ont pour professeur Descartes qui les définit comme une paire de nombres réels (partie réelle et partie imaginaire), tandis qu'une autre partie des étudiants a pour professeur Bessel qui les définit par leurs coordonnées polaires. Chacun définit ensuite les notions usuels sur les complexes. Puis au cours du semestre, les deux sections sont interchangées, et pourtant les étudiants ne rencontre aucune difficultés bien que les représentations concrètes choisies par chacun des enseignants soient distinctes. C'est cela l'essence des types de donnés abstraits.
Bon revenons à nos moutons, et à l'article qui critique ce principe pour des raisons de performances. Dans un premier temps, il ne fait pas que suivre ce principe mais applique toute la panoplie de la POO. Ensuite, au lieu de faire une hiérarchie de classes, il va définir un type somme de quatre figures et faire un switch statement pour comparer les performances. Ce qui revient à implémenter quelque chose du genre :
Maintenant, constatant que les formules de calculs de surfaces ont des similarité, il tente son optimisation ultime. En Ocaml cela ressemblerait à cela :
Il y a déjà un point problématique dans son code s'il ne cache pas la représentation de son type à l'extérieur : comment garantir l'invariant qu'un carré a nécessairement une largeur égale à sa hauteur ?
Ensuite au niveau des interfaces, chacun des deux modules peuvent se voir donner exactement la même :
moduletypeForme_simple=sigtypet(* constructeurs de formes *)valsquare:float->tvalrectangle:float->float->tvaltriangle:flaot->float->tvalcircle:float->t(* méthodes sur les formes *)valarea:t->floatend
Et maintenant, il y a même mieux entre ces deux modules : algébriquement, ces deux structures sont totalement isomorphes (comme pour Descartes et Bessel dans la fable de Reynolds). Autrement dit, elles sont complétement équivalentes pour l'utilisateur et, en tant qu'implémenteur, je peux passer de l'une à l'autre sans perturber aucunement le code client, à la condition de respecter le principe Code should not know about the internals of objects it’s working with. ;-)
Enfin, si l'on revient à mon graphe de la catégorie des structures algébriques correspondantes, comme pour le type Top il n'y a pas qu'une implémentation possible pour la somme de quatre structures. Ce qu'il a constaté c'est qu'en choisissant une implémentation adaptée, on peut avoir un code plus performant. En revanche, si on ne veut pas perturber le code client de nos modules, il faut cacher les détails d'implémentation. D'ailleurs, si seule la méthode area l'intéresse, je peux lui proposer un choix encore plus drastique pour la représentation du type t, à savoir type t = float :-P Autrement dit, on calcule une bonne fois pour toute la surface à la construction (ce qui ne marche réellement que si les valeurs sont immuables ;-)
Une dernière petite remarque pour la route : ces deux modules de formes simples ne sont, par contre, pas isomorphes à la structure Top de la catégorie (celle que l'on manipule en faisant de la POO). Elles n'en sont que des sous-structures, car elles ne contiennent pas toutes les formes possibles et imaginables mais seulement quatre. Ce qui signifie que toutes le formes simples sont des formes, notion qui n'a absolument rien à voir avec l'héritage et une hiérarchie de classes (mais c'est là une toute autre histoire... ;-).
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: l'héritage et les exemples pourris
Posté par kantien . En réponse au lien « Clean code » : performances lamentables. Évalué à 4.
La semaine dernière, je n'avais pas assez de temps pour développer ma pensée et revenir sur le fond de l'article du lien; aujourd'hui je le peux.
Je voudrais d'abord revenir sur un point qu'il rejette : l'encapsulation ou les types de données abstraits, résumés sous le principe "Code should not know about the internals of objects it’s working with". C'est là un principe que je ne renierai jamais, et dont les développeurs OCaml font un usage abondant : on s'en fout, la plupart du temps, de la représentation concrète d'un type, ce que l'on veut connaître c'est son interface ou API, c'est-à-dire ce que l'on peut faire avec.
J'ai toujours aimé la présentation qu'en faisait John C. Reynolds dans son article Types, abstraction and parametric polymorphism. Il présente l'idée sous la forme d'une fable où des étudiants d'université suivent des cours sur les nombres complexes. Certains étudiants ont pour professeur Descartes qui les définit comme une paire de nombres réels (partie réelle et partie imaginaire), tandis qu'une autre partie des étudiants a pour professeur Bessel qui les définit par leurs coordonnées polaires. Chacun définit ensuite les notions usuels sur les complexes. Puis au cours du semestre, les deux sections sont interchangées, et pourtant les étudiants ne rencontre aucune difficultés bien que les représentations concrètes choisies par chacun des enseignants soient distinctes. C'est cela l'essence des types de donnés abstraits.
Bon revenons à nos moutons, et à l'article qui critique ce principe pour des raisons de performances. Dans un premier temps, il ne fait pas que suivre ce principe mais applique toute la panoplie de la POO. Ensuite, au lieu de faire une hiérarchie de classes, il va définir un type somme de quatre figures et faire un switch statement pour comparer les performances. Ce qui revient à implémenter quelque chose du genre :
Maintenant, constatant que les formules de calculs de surfaces ont des similarité, il tente son optimisation ultime. En Ocaml cela ressemblerait à cela :
Il y a déjà un point problématique dans son code s'il ne cache pas la représentation de son type à l'extérieur : comment garantir l'invariant qu'un carré a nécessairement une largeur égale à sa hauteur ?
Ensuite au niveau des interfaces, chacun des deux modules peuvent se voir donner exactement la même :
Et maintenant, il y a même mieux entre ces deux modules : algébriquement, ces deux structures sont totalement isomorphes (comme pour Descartes et Bessel dans la fable de Reynolds). Autrement dit, elles sont complétement équivalentes pour l'utilisateur et, en tant qu'implémenteur, je peux passer de l'une à l'autre sans perturber aucunement le code client, à la condition de respecter le principe Code should not know about the internals of objects it’s working with. ;-)
Enfin, si l'on revient à mon graphe de la catégorie des structures algébriques correspondantes, comme pour le type Top il n'y a pas qu'une implémentation possible pour la somme de quatre structures. Ce qu'il a constaté c'est qu'en choisissant une implémentation adaptée, on peut avoir un code plus performant. En revanche, si on ne veut pas perturber le code client de nos modules, il faut cacher les détails d'implémentation. D'ailleurs, si seule la méthode
areal'intéresse, je peux lui proposer un choix encore plus drastique pour la représentation du typet, à savoirtype t = float:-P Autrement dit, on calcule une bonne fois pour toute la surface à la construction (ce qui ne marche réellement que si les valeurs sont immuables ;-)Une dernière petite remarque pour la route : ces deux modules de formes simples ne sont, par contre, pas isomorphes à la structure Top de la catégorie (celle que l'on manipule en faisant de la POO). Elles n'en sont que des sous-structures, car elles ne contiennent pas toutes les formes possibles et imaginables mais seulement quatre. Ce qui signifie que toutes le formes simples sont des formes, notion qui n'a absolument rien à voir avec l'héritage et une hiérarchie de classes (mais c'est là une toute autre histoire... ;-).
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.