Utilisez un Factory Pattern pour gérer les données de deux API différentes
Grande nouvelle, notre projet Filmo Patterns va accueillir des films provenant d’une deuxième source de données. Pour vous simplifier la vie, j’ai créé cette issue GitHub. Je vous invite à la découvrir avant de passer à la suite.
C’est bon, vous avez bien lu l’issue ? Alors, on peut continuer. ;)
Voici un résumé de notre problématique :
Nous avons un premier fichier JSON (
data/new-movie-data.json
). C’est grâce à ce fichier que nous affichons actuellement nos films d’Arnold Schwarzenegger sur le projet.Nous avons un deuxième fichier JSON (
data/external-movie-data.json
). C’est ce deuxième fichier que nous devons intégrer à notre projet. Il comprend des films de Sylvester Stallone.
Il y a aussi le fichier data/old-movie-data.json
, non ?
Oui, tout à fait !
J’ai gardé ce fichier pour vous montrer un cas d’exemple dans la suite de ce chapitre. Mais encore un peu de patience ! Pour le moment, je souhaite que vous vous posiez la question : comment peut-on intégrer ce nouveau fichier JSON sans avoir à modifier grandement notre code ?
Est-ce qu’on ne peut pas modifier la classe templates/MovieCard.js
? Quitte à en créer une nouvelle ?
Non, parce que pour le coup, ce fichier nous sert plutôt de template (on dit aussi vue), et affiche les données. Il ne doit pas vraiment contenir de la logique, et plus particulièrement de la logique de formatage. Son rôle est uniquement d’afficher l’affiche du film, son nom, sa durée et sa date de sortie.
OK, est-ce qu’on ne pourrait pas ajouter de nouvelles règles dans notre Constructor Pattern models/Movie.js
?
Oui mais non. :)
Sur le papier, ça peut sembler être une bonne idée, parce que tout notre formatage est réalisé à l’intérieur de ce fichier. Cela dit, ça risque d’alourdir le code de la classe et de le rendre aussi moins lisible.
Voilà ce que nous allons faire : nous allons créer un nouveau Constructor Pattern pour gérer ce nouveau formatage de données, et nous allons nous servir d’un Factory Pattern pour l’implémenter.
Identifiez les caractéristiques du Factory Pattern
Dans le diagramme ci-dessus, vous pouvez voir que le Factory Pattern est composé des éléments suivants :
La Factory. Elle va récupérer les données qui lui seront passées, et va déléguer la création et le formatage de ces données au bon Constructor. Ici, les données sont celles des fichiers
new-movie-data.json
etexternal-movie-data.json
.Les objets qui vont être créés par la Factory. Dans notre cas, nous allons avoir deux Constructor Patterns. Un qui existe déjà (Movie.js) et l’autre qui reste à créer. Ils vont formater la donnée pour nous. Ils vont l’envoyer dans notre fichier de template
MovieCard
sans que ce dernier ait de traitements supplémentaires à réaliser.
Du coup, ce design pattern permet de déléguer la création d’un objet ?
C’est ça !
Le rôle du Factory Pattern est de faciliter la création d’objets au sein d’une base de code. On va s’en servir pour créer et gérer différents types d’objets qui présentent des caractéristiques similaires. Dans la pratique, on peut, par exemple, s’en servir pour :
Gérer différentes sources de données. Dans le cas de notre application, ces données proviennent de deux fichiers différents. Cela dit, ces données pourraient aussi venir d’une API et d’une base de données ;
Gérer des basculements de données. Autrement dit, passer de données mockées (comme c’est le cas actuellement dans notre projet) à des données provenant d’une API.
Et du coup, côté code, ça donne quoi ?
Alors, côté Factory, le code est assez “simple” :
class MoviesFactory {
constructor(data, type) {
// Si le type correspond à l'ancienne API, alors retourne-moi l'ancien formatage
if (type === 'oldApi') {
return new OldMovie(data)
// Sinon retourne-moi le nouveau formatage
} else if (type === 'newApi') {
return new Movie(data)
// Une bonne pratique est de déclencher une erreur si le format n'est pas reconnu
} else {
throw 'Unknown type format'
}
}
}
Je retourne le Constructor OldMovie
ou Movie
en fonction du type d’objets que j’ai passés. Ici, le paramètre type
peut être oldApi
ou newApi
. Puis dans mon fichier App.js
, au lieu d’appeler directement le constructor Movie
ou OldMovie
, j’appelle ma Factory :
const OldMovies = oldMoviesData
.map(movie => new MoviesFactory(movie, 'oldApi'))
Implémentez un Factory Pattern
Pour implémenter un Factory Pattern, j’ai tendance à créer un nouveau dossier, que je vais appeler factories
. Et je vais aussi créer un fichier MoviesFactory
.
Bon, comme toujours, rien ne remplace la pratique !
Nous allons implémenter un Factory Pattern pour afficher les films contenus dans les fichiers data/old-movie-data.json
et data/new-movie-data.json
.
Mais les films vont s’afficher en double, non ?
Tout à fait. :)
Mais ça va me permettre de vous montrer un exemple d’implémentation. À la fin du chapitre, ce sera à vous de jouer !
Si vous avez bien suivi la vidéo, votre code devrait être conforme au code de la branche partie-2/chapitre-2-exercice
.
À vous de jouer !
OK, c’est maintenant à vous de jouer !
Vous vous souvenez de l’issue GitHub en début de chapitre ? Vous allez devoir l’implémenter !
Si je résume cette issue, voici ce que vous devez faire :
Utilisez les données contenues dans le fichier
data/external-movie-data.json
. Ces données correspondent à des films de Sylvester Stallone. Vous allez donc devoir créer dans le fichierApp.js
un nouvel appel API via notre classeMovieApi
. Vous pouvez vous inspirer des appels API déjà présents dans le fichier.Créez un Constructor Pattern pour formater les données récupérées. Vous ne devez pas toucher au fichier
templates/MovieCard.js
.Mettez à jour le Factory
MoviesFactory
pour qu’il vous retourne le bon objet.
Allez, c’est pas mal de boulot, mais tout va bien se passer. :)
Une fois que vous aurez trouvé la solution, ou si vous êtes bloqué, je vous invite à regarder ma correction dans la vidéo ci-dessous.
Le code source contenant la solution de cet exercice se trouve sur la branche partie-2/chapitre-2-fin
.
En résumé
Le Factory Pattern est utilisé pour déléguer la création d’objets. Au lieu de créer vos objets “en direct”, vous passez par un autre objet (la Factory) qui va se charger de créer le bon objet.
Pour créer un Factory Pattern, vous devez créer un dossier
factories
et instancier les objets directement depuis le bon constructeur de la Factory.Attention à l’utilisation de ce dernier, qui a tendance à fortement complexifier la base de code.
Vous connaissez maintenant votre deuxième design pattern, et surtout vous savez comment implémenter deux design patterns en même temps ! Dans le prochain chapitre, vous découvrirez un design pattern très commun : le Singleton !