29
juil.
2009
par
Styx31
Classé sous : Développement, My Life
C’est ce que je me suis finalement dis après avoir passé mes deux journées sur cette pauvre fonctionnalité.
Tout partait d’une volonté louable : implémentation d’une “petite” fonctionnalité en apparence assez simple. Comme souvent en fait.
Dans mon cas, il s’agissait de rendre virtuel le chargement d’une grosse liste dans une gridview. La technique est assez bien rodée : passage par une classe qui se charge de découper la liste en pages, et lorsque la gridview demande une ligne, on cherche si la page correspondante a été chargée. Si ce n’est pas le cas, on interroge le service, on stocke le résultat, et ça roule… J’avais d’ailleurs déjà développé une telle fonctionnalité pour un autre endroit de l’application.
Dans ma volonté de faire du réutilisable, je me suis dit que c’était donc le bon moment pour créer une classe générique implémentant capable de faire un tel traitement. Je commence donc à prototyper une classe DeferredList<T>, en gardant en tête que je souhaitais quelque chose de simple, avec peu de calculs et peu de variables pour conserver une empreinte mémoire faible (étant donné que l’on allait déjà avoir une liste assez conséquente en taille).
Mais j’avais plus ou moins involontairement oublié un aspect de cette gridview (et qui n’existait pas dans mon cas précédent) : elle permet d’ajouter ou de supprimer des éléments. Soit. Sauf que non, mon prototype omettait un détail : les modifications apportées à la liste ne sont envoyées au serveur qu’à un moment précis, lorsque l’utilisateur souhaite enregistrer les modifications apportées. Entre temps, le chargement des pages continue de me renvoyer la liste non modifiée.
Du coup, lorsque l’on supprime un élément (par exemple au début de la grille, dans la première page), cela décale automatiquement toutes les pages qu’il faudra charger. Exemple si dessous avec la suppression de l’élément situé à la ligne de la gridview d’index 2.
Imaginons que les pages 2 et 3 n’aient pas été chargées : avant l’opération, la demande de l’élément affiché à la ligne d’index 9 provoque le chargement de la page 2, et après l’opération celui de la page 3.
L’ajout provoque forcément l’effet inverse.
J’ai donc modifié mon code pour gérer ce cas. Ça commençait déjà à sentir le sapin : répercuter le décalage sur toutes les pages suivantes dans le cas de listes avec plusieurs milliers de pages ne me plaisait pas.
Et la baleine commença à apparaître : la gestion de la page renvoyée par l’évènement ne correspondait donc pas forcément à l’emplacement réel des éléments. Exemple avec le cas précédent, en cas de chargement de la page :
Comment savoir que l’élément d’index 2 devait être ignoré et que les suivants devaient être décalés ? A part stocker dans chaque page les éléments supprimés (et leur index)...
Voilà donc comment, à force de réécrire du code, on se retrouve à perdre sa journée. La solution existe bien, mais elle ressemble désormais à une énorme baleine au lieu d’un simple petit gravier blanc.
Conclusion : Shelve, Undo changes, et s’efforcer d’oublier le code écrit pour repartir de zéro sur quelque chose intégrant dès le départ ce type de contrainte, et voir si quelque chose de simple reste possible. Si c’est le cas je partagerai ma solution.
Note : Et pour la forme, on risque finalement d’abandonner la possibilité d’interagir avec la liste directement lorsqu’elle dépasse un certain nombre d’éléments, tant le rapport intérêt utilisateur/développement semble faible. A priori les modifications seront immédiatement envoyées au serveur (la suppression d’un seul élément sera impossible) et la liste des éléments ne sera plus affichée.