Collection accessors considered harmful

L’art du design objet repose sur la détermination des rôles et responsabilités de chaque classe. Quelle classe est responsable de la gestion de telle ou telle donnée ? A qui et sous quelles conditions ces données sont-elles exposées ? Il est bon de se poser ces questions souvent et de questionner son modèle objet tout au long de sa conception.
Actuellement, la mode est aux POJOS. Depuis quelques années, depuis Hibernate et Spring, tout le monde a appris à programmer comme ça. On crée une classe, on déclare ses champs privés, et on génère des accesseurs : un coup de bouton droit dans Eclipse, et le tour est joué – easy as pie.
Le problème, c’est que ce réflexe conditionné est dangereux dans le cas des collections, car il affaiblit l’encapsulation de la classe.
Le code suivant illustre le problème.
En fournissant des accesseurs directs sur la collection d’Items, la classe Caddie expose son fonctionnement interne, et perd le contrôle de l’ajout ou de la suppression d’éléments dans le Set. Non seulement l’encapsulation est rompue, mais il devient plus difficile de changer l’implémentation de la classe, par exemple pour modifier le type de collection utilisée.

  1. public class Caddie
  2. {
  3. private Set<Item> items = new LinkedHashSet<Item>();
  4. public Set<Item> getItems()
  5. { return this.items;
  6. }
  7. public void setItems(Set<Item> items)
  8. { this.items = items;
  9. }
  10. }

La solution suivante permet de reprendre le contrôle sur la collection d’Items. Elle est basée sur deux principes :

  • La collection exposée est une copie en lecture seule. Son type est Collection afin de découpler son implémentation interne de sa représentation externe.
  • Des méthodes sont fournies (addItem() et removeItem()), qui permettent de gérer les Items. Mais cette fois, il est nécessaire de passer par la classe Caddie, qui reprend donc le contrôle de ses données. De plus, son implémentation est désormais masquée, améliorant son encapsulation et sa maintenabilité.
  1. public class Caddie
  2. {
  3. private Set<Item> items = new LinkedHashSet<Item>();
  4. public Collection<Item> getItems()
  5. { return Collections.unmodifiableSet(items);
  6. }
  7. public boolean addItem(Item item)
  8. { return items.add(item);
  9. }
  10. public boolean removeItem(Item item)
  11. { return items.remove(item);
  12. }
  13. }

La prochaine fois que vous générez des accesseurs pour vos POJOs, pensez à porter un regard critique sur leur encapsulation : ne laissez pas Eclipse réfléchir pour vous !

3 pensées sur “Collection accessors considered harmful

  • 16 mars 2009 à 14 h 18 min
    Permalink

    Je partage complètement votre point de vue…

    Par contre avec la démocratisation des annotations jpa, hibernate, ou autre orm… comment procéder sur un bean POJO possédant une collection de ce type ?

    On peu peut être annoter la variable d’instance privé de la collection, et non son getter, pour ensuite gérer les accesseurs à la collection avec votre méthode, add & remove ?

    Il serait vraiment intéressant de dérouler votre point de vue avec un exemple de bean à rendre persistant 🙂

    Merci

    Répondre
  • 6 avril 2009 à 1 h 02 min
    Permalink

    Pour ma part, j’ai pris l’habitude de déclarer la Collection en « final » et de ne fournir qu’un getter.

    Avantage : toutes les fonctions (clear, addAll…) de la Collection sont utilisables.
    Inconvénient : pas de restriction sur les fonctions utilisables, pas de traçage possible des actions sur la collection.

    Pour les frameworks qui exigent un setter, j’utilise le bricolage suivant :

    this.collection.clear();
    this.collection.addAll(collection);

    Répondre
  • 10 avril 2009 à 21 h 57 min
    Permalink

    Mickaël, la rupture du principe d’encapsulation provient du fait qu’une collection est un objet mutable (par opposition à une String par exemple qui est immutable). Donc déclarer un attribut final ne sert à rien, tout comme ne fournir qu’un getter. Si j’ai accès au getter, j’ai accès à la collection et je peux modifier son contenu sans même que l’objet qui la porte en soit informé. C’est ça la rupture d’encapsulation. Par contre, si j’appelle un setter avec une nouvelle collection, l’objet portant la collection sait qu’on lui affecte un nouvel objet. Dans ce cas, il n’y a pas de problème.

    Enfin, Olivier met l’accent sur un point bien anecdotique de la persmissivité de Java. Si on n’avait qu’à corriger cela au quotidien, ça serait beau. Malheureusement (ou heureusement?), la réalité est souvent bien pire…

    Répondre

Répondre à Alexandre de Pellegrin Annuler la réponse.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

%d blogueurs aiment cette page :