• 10 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 02/03/2022

Intégrez une nouvelle fonctionnalité à un objet existant avec le Decorator Pattern

Vous vous souvenez que dans la première partie du cours, vous avez découvert la notion de prototype ? Je vous avais dit que le moteur de la création d’objets était centré sur cette notion. Mon petit doigt me dit que vous allez aborder ce sujet dans ce chapitre. ;)

Permettez à vos utilisateurs de lancer une vidéo avec l’aide d’un Decorator

Dans ce chapitre, vous allez voir comment implémenter un Decorator pour ajouter une fonctionnalité à un objet. Comme pour les précédents chapitres, j’ai créé une issue sur GitHub.

C’est bon, vous l’avez vue ? Alors, on peut continuer ! :)

Si je résume la situation avec cette nouvelle issue :

  • Certains films, ceux provenant du fichier   data/external-movie-data.json  , contiennent maintenant un champ   trailer_id  . Ce champ correspond à l’ID YouTube de la vidéo. Au clic, une fenêtre modale s’ouvre, et on peut voir le teaser du film.

  • Les autres films, ceux provenant du fichier   data/new-movie-data.json  , n’ont pas cette fonctionnalité.

J’ai créé un nouveau fichier que j’ai appelé   templates/PlayerModal.js  . Ce fichier contient notamment le code ci-dessous.

const player = `
           <div class="player">
               <iframe
                   height="600"
                   width="800"
                   src=${this.movie.trailer}
               ></iframe>
               <button class="close-btn">Fermer la fenêtre<button>
           </div>
       `

C’est ce morceau de code qui va me permettre d’afficher mon iFrame comme dans la capture d’écran ci-dessous.

Screenshot de la page Filmo Patterns avec la vidéo teaser qui s'affiche
La fenêtre YouTube qui affiche le teaser du film

Ici, le challenge va être de vouloir ajouter une nouvelle fonctionnalité à l’objet   templates/MovieCard.js  en le modifiant le moins possible.

createMovieCard() {
       const movieCard = `
           <div class="movie-thumbnail center">
               <img
                   alt="${this._movie.title}"
                   src="${this._movie.thumbnail}"
               />
           </div>
           <h3 class="fs-16 center">${this._movie.title}</h3>
           <p class="fs-14 center">
               <span>${this._movie.released_in}</span>
               -
               <span>${this._movie.duration}</span>
           </p>
       `

       this.$wrapper.innerHTML = movieCard
       return this.$wrapper
   }

Vous pourriez tout à fait complexifier le code ci-dessus en lui ajoutant un traitement complémentaire, mais vous prendriez le risque de faire grossir encore et encore cet objet. Vous allez vous servir d’un Decorator pour implémenter cette solution. ;)

Identifiez les caractéristiques du Decorator Pattern

Le Decorator Pattern est composé d'un Client, d'un Component et d'un Decorator
Le Décorator Pattern et ses éléments

Un Decorator Pattern est composé de trois acteurs :

  • Le premier est leClient. C’est, ici, l’objet ou la fonction qui va appeler le Decorator. Dans le cas du projet, le client va être l’objetApp.

  • Le deuxième est le Component. C’est l’objet sans la nouvelle fonctionnalité. Pour Filmo Patterns, le component estMovieCard. Ici, cet objet ne sait pas qu’il est “décoré” (on pourrait aussi dire qu’il est surchargé).

  • Le troisième acteur est leDecorator. Cet objet va récupérer un objet existant, le surcharger (en lui ajoutant une nouvelle fonctionnalité), et va le retourner.

Ce design pattern est très répandu dans le monde de la programmation : il permet de rapidement ajouter de nouvelles fonctionnalités à un objet sans avoir à le surcharger ou à le complexifier. Bon, bien sûr, il est important de garder une certaine rigueur lors de l’implémentation d’un Decorator : n’essayez surtout pas de surcharger un Decorator qui surcharge un Decorator et ainsi de suite. ;)

Alors, comment se présente un Decorator ?

Jetez un coup d'œil au code ci-dessus : ;)

class Movie {
   constructor(title, duration) {
       this._title = title
       this._duration = duration
   }

   createMovieTitle() {
       return `
           <h1 class="movie-title">${this._title}</h1>
       `
   }
}

function movieWithAgeLimit(movie, ageLimit) {
   // Grâce au prototypage, je peux facilement ajouter de nouvelles propriétés et méthodes sur un objet existant
   movie.ageLimit = ageLimit

   movie.canSeeMovie = () => {
       console.log(`Vous devez avoir plus de ${movie.ageLimit} pour voir ${movie._title}`)
   }

   // Ici le return est important, c'est ce qui me permet de retourner une version modifiée de l'objet Movie
   return movie
}

// J'instancie un objet Movie "simple"
const MovieWithoutAgeLimit = new Movie('Batman Returns', 130)

// J'instancie un objet Movie décoré
const MovieWithAgeLimit = movieWithAgeLimit(new Movie('Alien', 120), 16)

Dans le code ci-dessus, la fonction  movieWithAgeLimit  me permet rapidement et simplement d’ajouter de nouvelles fonctionnalités à un objet existant. C’est rendu possible grâce au prototypage !

Implémentez un Decorator Pattern

Pour implémenter un Decorator, je vais le plus souvent créer un dossier decorators, ou je peux même le mettre dans le même fichier que mon objet existant. Le nommage du   Decorator  est important : essayez de reprendre celui de l’objet surchargé en précisant la fonctionnalité que vous ajoutez. Dans l’exemple ci-dessus, pour créer une limite d’âge, j’ai nommé ma fonction   movieWithAgeLimit.

Dans la vidéo ci-dessous, je vais vous montrer comment intégrer un Decorator Pattern sur un objet   User  .

Vous pouvez retrouver le code de cette implémentation sur ce Gist GitHub.

Comme vous pouvez le constater, un Decorator est un design pattern simple et pratique à mettre en place. Je pense que c’est l’un des premiers que vous pouvez mettre en pratique dans vos projets.

À vous de jouer !

Dans cette section, je vais vous demander de résoudre notre issue GitHub. Vous allez donc devoir implémenter un Decorator pour l’objet   templates/MovieCard.js  . J’ai déjà créé :

  • Le fichier   templates/PlayerModal.js  . C’est ce fichier qui va vous permettre d’afficher l’iframe YouTube.

  • Le fichier   decorators/movieCardWithPlayer.js  . C’est ce fichier qui va contenir le decorator ; j’ai aussi déjà créé la fonction et je l’ai ajoutée dans le fichier   index.html  .

Voilà, il ne vous reste plus qu’à mettre en pratique. Une fois que vous avez suffisamment cherché et que vous avez trouvé la solution (ou au contraire que vous butez un peu), vous pouvez regarder la vidéo ci-dessous !

Le code source contenant la solution de cet exercice se trouve sur la branche partie-3/chapitre-2-fin  .

En résumé

  • Un Decorator permet d’ajouter une fonctionnalité à un objet existant sans avoir à la complexifier. Il prend en paramètre l’objet en question, le modifie et le retourne.

  • En JavaScript, la création de Decorator est fortement simplifiée grâce au prototypage. C’est ce mécanisme qui nous permet d’ajouter des propriétés et méthodes à un objet.

  • Il est possible que vous voyiez parfois des Decorators implémentés avec un   @  . Cela dit, ce mécanisme reste expérimental aujourd’hui en JavaScript.

Et de deux !

Vous venez d’apprendre votre deuxième Structural Design Pattern et vous commencez maintenant à avoir une batterie d’outils à votre disposition pour maintenir votre codebase. Dans le prochain chapitre, nous allons voir le Proxy Pattern, qui va nous permettre de faciliter la mise en cache. :)

Exemple de certificat de réussite
Exemple de certificat de réussite