Répartissez efficacement les responsabilités : utilisez ce pattern pour simplifier l'utilisation de vos collections en répartissant les responsabilités de façon plus claire.

Le pattern "First Class Collection" a été proposé en 2008 par Jeff Bay (programmeur chez ThoughtWorks) dans un chapitre du livre The Thoughtworks Anthology :

"Application of this rule is simple: any class that contains a collection should contain no other member variables. Each collection gets wrapped in its own class, so now behaviors related to the collection have a home. You may find that filters become a part of this new class. Also, your new class can handle activities like joining two groups together or applying a rule to each element of the group."

Jeff Bay - The Thoughtworks Anthology

L'idée est simple et clairement exprimée : extraire toute l'intelligence associée à la gestion d'un liste dans sa propre classe

Ce pattern est extrêment intéressant à analyser car on peut le rattacher à plusieurs principes de programmation :

  • au principe de responsabilité unique (Single Responsability Principle),
  • au principe d'ouverture / fermeture (Open / Closed Principle)
  • au "principe" de Feature Envy (smell code)

Single responsability principle, open / closed principle, Feature Envy... OK, intéressant, mais pourquoi ? comment ?

Un peu d'histoire

Avant l'avènement des génériques dans C# 2.0 en 2005, voici un exemple de code utilisant une liste :

public class Carrier {

  private readonly List _tanks = new List();

  // ...
    Tank tank = (Tank)_tanks[index];
  // ...
    _tanks.Add(new Not_a_tank_at_all());

On peut voir facilement plusieurs problèmes associés au code ci-dessus :

  • l'obligation de devoir caster à chaque fois l'objet récupéré à partir de la liste,
  • plus grave, la possibilité d'ajouter autre chose que ce qui était prévu dans la liste

Dans le cas simpliste ci-dessus on peut arguer le fait qu'au sein d'une classe l'encapsulation est suffisante pour gérer ces problèmes. Bien sûr. Mais dès que la liste passe d'un objet à un autre via des paramètres de méthodes, on perd alors tout contrôle...

Le code suivant permettait de régler ces problèmes :

public class TankCollection {

  private readonly List _collection = new List();

  public Tank this[int index] {
    get { return (Tank)_collection[index];
  }

  public void Add(Tank tank) {
    _collection.Add(tank);
  }
}

On voit le rôle essentiel de cette First Class Collection : gérer l'encapsulation de la liste sous-jacente pour s'assurer de la cohérence des données qui y sont ajoutées.

Une nouvelle ère

En .Net, avant l'avènement des génériques, le pattern aurait pu perdre son intérêt s'il était limité à résoudre les problèmatiques que l'on vient de décrire.

Dans son explication du pattern, Jeff Bay indique bien que cette classe va également regrouper tous les comportements relatifs à la gestion de la collection sous-jacente : recherche, tri, règle d'insertion, ...

Ainsi, la responsabilité unique de cette classe sera de fournir une interface orienté métier pour accéder à la collection .

#tobecontinued