Gestion des données : changement de paradigme

La librairie rnb-php possède de nombreuses classes qui manipulent des données stockées dans des fichiers sous différents formats ; il existait donc différentes variantes en fonction du format géré : solution qui atteint vite ces limites, d'où un changement de paradigme...

Imaginons une classe d'objets Foo ; peu importe ce que ces objets font, ils doivent le faire en récupérant ou en écrivant des données dans des fichiers dont le format peut varier selon les convenances (ou la disponibilité). Pour gérer cette situation, une première approche à consister à créer des classes héritant de la classe principale et spécialisées dans un format de données :

// Classe initiale
class Foo {}
// Foo qui gère les données en json
class FooJson extends Foo {}
// Foo qui gère les données en xml
class FooXml extends Foo {}
Classe Foo et ses dérivés pour différents formats de données.

J'arrivais a peu près à obtenir ce que je souhaitais, surtout grâce à l'utilisation de fabriques (Factory). Mais le système peut rapidement atteindre ces limites, par exemple quand on souhaite avoir un nouvelle classe d'objets dérivant de la classe de base, et qu'il faut donc créer autant de nouvelles versions pour chaque format supporté :

// Bar enrichit Foo
class Bar extends Foo {}
// Bar qui gère les données en json
class BarJson extends Bar {}
// Bar qui gère les données en xml
class BarXml extends Bar {}
Classe Bar et ses dérivés pour différents formats de données.

Hiérarchie de classes

Hiérarchie des classes en mode « est un ».

On peut comprendre le problème : si on prend un format défini, json par exemple, on voit que les traitements particuliers effectués dans FooJson ne peuvent pas être repris par héritage dans BarJson, qu'il faut donc recopier tout le code. Le paradigme « héritage » (« est un ») n'est plus forcément pertinent.

Pour contourner le problème, prenons exemple sur le monde des sciences : quand un paradigme ne remplit plus correctement sa fonction, on le change ! Dans le cas qui nous occupe, il suffit de considérer que la gestion des formats de données n'est plus une propriété des objets mais la tâche principale d'un objet qu'ils utiliseront. On construit donc des fournisseurs (Provider), un pour chaque format de données, et on passe d'une conception « est un » à une conception « a un » :

// Classe initiale
class Foo {}
// Fournisseur de données json utilisé par Foo
class FooJsonProvider {}
// Fournisseur de données xml utilisé par Foo
class FooXmlProvider {}
// Bar enrichit Foo
class Bar extends Foo {}
// Fournisseur de données json utilisé par Bar
class BarJsonProvider extends FooJsonProvider {}
// Fournisseur de données xml utilisé par Bar
class BarXmlProvider extends BarJsonProvider {}
La gestion des formats de données déléguée à des objets dédiés.

Hiérarchie de classes

Hiérarchie des classes en mode « a un ».

Au final, le nombre de classes à gérer / créer est le même (6), mais le travail effectuer dans FooJsonProvider par exemple pourra être récupéré dans BarJsonProvider. Et si l'on écrit des fournisseurs de données plus génériques, on peut même éviter cette cascade d'héritage.