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