Magazine

Les Hooks ou l'extensibilité d'un objet / programme

Publié le 23 avril 2008 par Olivier Duval

Dans un précédent billet, j'exposais un cas particulier à traiter sur certaines pages.

Rappel des faits : un contrôle se charge d'aller chercher un flux RSS ou Atom afin d'afficher chaque élément sur une page Web afin d'obtenir à peu près ce résultat :

flux Atom

Les pages du site sont pour la plupart en UTF-8 (ou du moins le devraient), mais certaines (en raison d'un lourd existant), présentent un cas particulier : elles sont encodées en ISO-8859-1 (voire Windows-1252). La conséquence de cet encodage est un défaut d'affichage pour certains caractères issus du flux et encodés en UTF; ce qui donne ce mauvais résultat (remarquez les ?) :

bad flux Atom

Solution simple et rapide qui vient à l'esprit : convertir les caractères dans leur équivalent ISO dans le contrôle.

Cette solution ne satisfait pas le cas commun des pages en UTF, il faudra ajouter au contrôle une propriété qui précisera si on veut ou non la conversion en ISO le cas échéant. Ce qui est gênant dans cette rustine, c'est que pour traiter un cas particulier, nous sommes obligés d'apposer une propriété au contrôle, et ce potentiellement pour chaque cas spécifique : à force, on va se retrouver à gérer pleins de cas, donc faire pleins de si(propriete_A) alors faire traitement particulier, ce qui n'est pas forcément le rôle du contrôle (qui devrait traiter le cas général uniquement), la solution n'est pas élégante dira-t-on.

Alors comment rendre extensible mon contrôle sans pour autant qu'il devienne illisible : en donnant la possibilité d'appliquer un hook au moment de l'affichage des éléments du flux.

Alors, qu'est qu' un hook, selon cette définition :

(...) fonction permettant une plus grande facilité dans l'adjonction de fonctionnalités nouvelles au programme

C'est juste ce qu'il nous faut, une technique simple à mettre en oeuvre, et qui conserve le principe du ouvert-fermé en conception objet.

En C#, les événements (voire les délégués associés) répondent notamment à ce besoin : autoriser des clients à s'enregistrer sur un objet afin de déclencher des méthodes pour du traitement spécifique. Les événements pourraient aussi être utilisés pour établir une communication inter-objets : qu'un objet serveur puisse donner de l'information à ses clients abonnés, par exemple, sur l'état d'avancement d'un processus.

Notre classe contrôle offre alors un événement sur lequel s'abonner, le contrôle déclenchera alors l'événement au moment opportun, ici lors de l'affichage des données, et une méthode pour déclencher manuellement l'événement si besoin de la part des clients de l'objet :

  1. event EventHandler Displaying;
  2. void OnDisplaying(Item it, EventArgs arg);

Par défaut, on utilise EvenHandler, car peu d'informations sont à échanger, on pourrait bien entendu implémenter une classe qui dérive d'EventHandler si l'on souhaitait y mettre plus d'informations non directement liées à l'objet à traiter (ici l'élément du flux, la classe Item) : un état, un contexte d'utilisation, ...

Il suffit alors de s'abonner à l'événement du contrôle à partir de ma page aspx, et de mettre le traitement ad-hoc souhaité, ici, la conversion des caractères :

  1. private void InitializeComponent()
  2. {
  3. moncontrole.Displaying += new EventHandler(_default_Displaying);
  4. }
  5. void _default_Displaying(object sender, EventArgs e)
  6. {
  7. if (sender != null &
  8. sender is Item)
  9. {
  10. ((Item)sender).Title = UTFtoWin1252(((Item)sender).Title);
  11. ((Item)sender).Description = UTFtoWin1252(((Item)sender).Description);
  12. }
  13. }

dans le contrôle on déclenchera l'événement à chaque affichage d'une élément du flux, dans le CodeFile/CodeBehind du contrôle :

  1. // on expose la possibilité d'un branchement à l'événément Displaying
  2. public event EventHandler Displaying;
  3. // pour le déclenchement manuel de l'événément
  4. public void OnDisplaying(Item it, EventArgs arg)
  5. {
  6. // si branchement alors on exécute la méthode cliente attachée
  7. if (Displaying != null)
  8. Displaying(it, arg);
  9. }

et dans l'ascx du contrôle, on pourrait avoir dans le repeater chargé d'afficher les éléments du flux, le déclenchement de l'événement :

  1. <script language="C#" runat="server">
  2. private Item _doDisplaying(Item it)
  3. {
  4. // déclenchement manuel de l'événement
  5. OnDisplaying(it, new EventArgs());
  6. return it;
  7. }
  8. </script>
  9. <div id="flux">
  10. <asp:Repeater ID="rpRss" runat="server">
  11. <HeaderTemplate><ul class="item"></HeaderTemplate>
  12. <ItemTemplate>
  13. <li class="itemelt">
  14. <p>
  15. <span class="itemtitle">
  16. <a class="itemlink""
  17. href="<%# _doDisplaying((Item)Container.DataItem).Link %>"><%# _doDisplaying((Item)Container.DataItem).Title)%></a>
  18. </span>
  19. <span class="itemdate">(<%# string.Format("{0:dd/MM/yyyy}", _doDisplaying((Item)Container.DataItem).DatePublication)%>)</span>
  20. </p>
  21. <p>
  22. <span class="itemdescription">
  23. <%# _doDisplaying((Item)Container.DataItem).Description)%>
  24. </span>
  25. </p>
  26. </li>
  27. </ItemTemplate>
  28. <FooterTemplate></ul></FooterTemplate>
  29. </asp:Repeater>
  30. </div>

Les avantages de cette solution :

  • le contrôle n'est pas pollué de cas particuliers à traiter, on rend la responsabilité au(x) client(s) du contrôle de traiter le cas.
  • va avec le 1er point, on évite pleins de if et donc plein de code à maintenir
  • on étend le comportement de la classe du contrôle sans la modifier
  • on peut décider de créer autant de hooks que l'on souhaite - on pourrait imaginer par exemple, un hook avant d'aller chercher le flux, après recherche du flux, etc...

Inconvénients :

  • les clients doivent connaitre un minimum quand est-ce que le hook sera déclenché, doit donc être documenté
  • coder des hooks inappopriés ou inutiles (en faire trop sans que cela serve forcément)

En génie logiciel, le plus difficile bien souvent c'est la maintenabilité du système, quelques bonnes pratiques permettent d'en réduire (un peu) la difficulté de la réaliser, encore faut-il y penser


Retour à La Une de Logo Paperblog

A propos de l’auteur


Olivier Duval 4 partages Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte