Comprendre les modèles de conception en JavaScript

Aujourd’hui, nous allons mettre nos chapeaux d’informatique à mesure que nous apprenons certains modèles de conception courants. Les modèles de conception offrent aux développeurs des moyens de résoudre les problèmes techniques de manière élégante et réutilisable. Intéressé à devenir un meilleur développeur JavaScript? Alors lisez la suite.

Tutoriel republié

Toutes les quelques semaines, nous revoyons certains des articles préférés de nos lecteurs tout au long de l'histoire du site. Ce tutoriel a été publié pour la première fois en juillet 2012..


introduction

Les modèles de conception solides constituent le bloc de construction de base des applications logicielles maintenables. Si vous avez déjà participé à un entretien technique, vous avez aimé avoir été interrogé à leur sujet. Dans ce tutoriel, nous allons examiner quelques modèles que vous pouvez commencer à utiliser aujourd'hui..


Qu'est-ce qu'un motif de conception??

Un modèle de conception est une solution logicielle réutilisable

En termes simples, un modèle de conception est une solution logicielle réutilisable pour un type de problème spécifique qui se produit fréquemment lors du développement de logiciels. Au cours des nombreuses années de développement de logiciels, les experts ont trouvé des moyens de résoudre des problèmes similaires. Ces solutions ont été encapsulées dans des modèles de conception. Alors:

  • les modèles sont des solutions éprouvées aux problèmes de développement de logiciels
  • les modèles sont évolutifs car ils sont généralement structurés et ont des règles que vous devez suivre
  • les motifs sont réutilisables pour des problèmes similaires

Nous allons entrer dans quelques exemples de modèles de conception plus loin dans le tutoriel.


Types de Design Patterns

En développement logiciel, les modèles de conception sont généralement regroupés en quelques catégories. Nous allons couvrir les trois plus importants dans ce tutoriel. Ils sont expliqués brièvement ci-dessous:

  1. Création les modèles se concentrent sur les moyens de créer des objets ou des classes. Cela peut sembler simple (et ce dans certains cas), mais les grandes applications doivent contrôler le processus de création d’objet..
  2. De construction Les modèles de conception se concentrent sur les moyens de gérer les relations entre les objets afin que votre application soit architecturée de manière évolutive. Un aspect clé des modèles structurels est de s'assurer qu'un changement dans une partie de votre application n'affecte pas toutes les autres parties..
  3. Comportementale les modèles se concentrent sur la communication entre les objets.

Vous avez peut-être encore des questions après avoir lu ces brèves descriptions. Cela est naturel, et les choses s’éclairciront une fois que nous examinerons en profondeur certains modèles de conception. Alors lisez la suite!


Une note sur les classes en JavaScript

Lorsque vous lisez sur les modèles de conception, vous verrez souvent des références à des classes et à des objets. Cela peut être déroutant, car JavaScript n'a pas vraiment la construction de "classe"; un terme plus correct est "type de données".

Types de données en JavaScript

JavaScript est un langage orienté objet dans lequel les objets héritent d'autres objets dans un concept appelé héritage prototypique. Un type de données peut être créé en définissant ce que l’on appelle une fonction constructeur, comme ceci:

fonction Person (config) this.name = config.name; this.age = config.age;  Person.prototype.getAge = function () return this.age; ; var tilo = nouvelle personne (name: "Tilo", age: 23); console.log (tilo.getAge ());

Notez l'utilisation de la prototype lors de la définition des méthodes sur le La personne Type de données. Depuis plusieurs La personne les objets référenceront le même prototype, cela permet au getAge () méthode à partager par toutes les instances du La personne type de données, plutôt que de le redéfinir pour chaque instance. En outre, tout type de données qui hérite de La personne aura accès à la getAge () méthode.

Traiter avec la vie privée

Un autre problème commun à JavaScript est qu’il n’ya pas de véritable sens des variables privées. Cependant, nous pouvons utiliser des fermetures pour simuler quelque peu la vie privée. Considérez l'extrait suivant:

var retinaMacbook = (function () // Variables privées var RAM, addRAM; RAM = 4; // Méthode privée addRAM = fonction (additionalRAM) RAM + = additionalRAM;; return // Variables et méthodes publiques USB: undefined , insertUSB: function (device) this.USB = device;, removeUSB: function () var device = this.USB; this.USB = undefined; return device;;) ();

Dans l'exemple ci-dessus, nous avons créé un rétinaMacbook objet, avec des variables et méthodes publiques et privées. Voici comment nous l'utiliserions:

retinaMacbook.insertUSB ("myUSB"); console.log (retinaMacbook.USB); // déconnecte "myUSB" console.log (retinaMacbook.RAM) // déconnecte undefined

Nous pouvons faire beaucoup plus avec les fonctions et les fermetures en JavaScript, mais nous n'entrerons pas dans les détails de ce didacticiel. Avec cette petite leçon sur les types de données JavaScript et la confidentialité, nous pouvons continuer à apprendre les modèles de conception..


Modèles de création

Il existe de nombreux types de modèles de création, mais nous allons en couvrir deux dans ce didacticiel: Constructeur et Prototype. Je trouve que ceux-ci sont utilisés assez souvent pour mériter l'attention.

Modèle de constructeur

Le modèle Builder est souvent utilisé dans le développement Web et vous l'avez probablement déjà utilisé auparavant sans vous en rendre compte. En termes simples, ce modèle peut être défini comme suit:

L'application du modèle de générateur nous permet de construire des objets en spécifiant uniquement le type et le contenu de l'objet. Nous n'avons pas à créer explicitement l'objet.

Par exemple, vous avez probablement déjà fait cela d'innombrables fois dans jQuery:

var myDiv = $ ('
C'est une div.
'); // myDiv représente maintenant un objet jQuery faisant référence à un nœud DOM. var someText = $ ('

'); // someText est un objet jQuery faisant référence à HTMLParagraphElement var input = $ ('');

Regardez les trois exemples ci-dessus. Dans le premier, nous avons passé dans un

élément avec un contenu. Dans le second, nous sommes passés dans un vide

étiquette. Dans le dernier, nous avons passé dans un élément. Le résultat des trois était identique: nous avons renvoyé un objet jQuery faisant référence à un nœud DOM.

le $ La variable adopte le modèle de générateur dans jQuery. Dans chaque exemple, nous avons renvoyé un objet DOM jQuery et avons eu accès à toutes les méthodes fournies par la bibliothèque jQuery, mais nous n’avons à aucun moment appelé explicitement document.createElement. La bibliothèque JS a géré tout cela sous le capot.

Imaginez tout le travail que nous ferions si nous devions créer explicitement l’élément DOM et y insérer du contenu! En utilisant le modèle de générateur, nous sommes en mesure de nous concentrer sur le type et le contenu de l'objet, plutôt que sur sa création explicite..

Modèle de prototype

Plus tôt, nous avons expliqué comment définir des types de données en JavaScript via des fonctions et en ajoutant des méthodes aux objets. prototype. Le modèle Prototype permet aux objets d’hériter d’autres objets, via leurs prototypes..

Le modèle prototype est un modèle dans lequel les objets sont créés à partir du modèle d'un objet existant par clonage..

C'est un moyen simple et naturel d'implémenter l'héritage en JavaScript. Par exemple:

var Person = numFeet: 2, numHeads: 1, numHands: 2; //Object.create prend son premier argument et l'applique au prototype de votre nouvel objet. var tilo = Object.create (Person); console.log (tilo.numHeads); // génère 1 tilo.numHeads = 2; console.log (tilo.numHeads) // génère 2

Les propriétés (et méthodes) dans le La personne objet se appliquer au prototype de la tilo objet. Nous pouvons redéfinir les propriétés sur le tilo objet si nous voulons qu'ils soient différents.

Dans l'exemple ci-dessus, nous avons utilisé Object.create (). Toutefois, Internet Explorer 8 ne prend pas en charge la méthode la plus récente. Dans ces cas, nous pouvons simuler son comportement:

var vehiclePrototype = init: function (carModel) this.model = carModel; , getModel: function () console.log ("Le modèle de ce véhicule est" + this.model); ; fonction véhicule (modèle) fonction F () ; F.prototype = vehiclePrototype; var f = new F (); f.init (modèle); retourne f;  var car = vehicle ("Ford Escort"); car.getModel ();

Le seul inconvénient de cette méthode est que vous ne pouvez pas spécifier de propriétés en lecture seule, qui peuvent être spécifiées lors de l’utilisation de Object.create (). Néanmoins, le modèle de prototype montre comment les objets peuvent hériter d'autres objets..


Modèles de conception structurelle

Les modèles de conception structurels sont vraiment utiles pour déterminer comment un système devrait fonctionner. Ils permettent à nos applications d'évoluer facilement et de rester maintenables. Nous allons examiner les modèles suivants dans ce groupe: Composite et Façade.

Modèle composite

Le motif composite est un autre motif que vous avez probablement déjà utilisé sans aucune réalisation..

Le motif composite indique qu'un groupe d'objets peut être traité de la même manière qu'un objet individuel du groupe..

Qu'est-ce que cela signifie? Eh bien, considérons cet exemple dans jQuery (la plupart des bibliothèques JS auront un équivalent à ceci):

$ ('.MyList'). addClass ('selected'); $ ('# myItem'). addClass ('sélectionné'); // ne fais pas cela sur de grandes tables, c'est juste un exemple. $ ("# dataTable tbody tr"). sur ("clic", fonction (événement) alert ($ (this) .text ());); $ ('# myButton'). on ("cliquez sur", fonction (événement) alert ("Cliquez sur".););

La plupart des bibliothèques JavaScript fournissent une API cohérente, qu'il s'agisse d'un seul élément DOM ou d'un tableau d'éléments DOM. Dans le premier exemple, nous pouvons ajouter le choisi classe à tous les articles ramassés par le .ma liste sélecteur, mais nous pouvons utiliser la même méthode pour traiter un élément DOM singulier, #myItem. De même, nous pouvons attacher des gestionnaires d’événements à l’aide du sur() méthode sur plusieurs nœuds ou sur un seul nœud via la même API.

En exploitant le modèle Composite, jQuery (et de nombreuses autres bibliothèques) nous fournit une API simplifiée..

Le motif composite peut parfois aussi causer des problèmes. Dans un langage faiblement typé tel que JavaScript, il peut souvent être utile de savoir s'il s'agit d'un seul élément ou de plusieurs éléments. Étant donné que le modèle composite utilise la même API pour les deux systèmes, nous pouvons souvent confondre l’un avec l’autre et aboutir à des bogues inattendus. Certaines bibliothèques, telles que YUI3, proposent deux méthodes distinctes pour obtenir des éléments (Y.one () contre Y.all ()).

Modèle de façade

Voici un autre modèle commun que nous prenons pour acquis. En fait, celui-ci est l'un de mes favoris parce que c'est simple, et je l'ai vu utilisé partout pour aider avec les incohérences du navigateur. Voici en quoi consiste le motif Facade:

Le motif de façade offre à l'utilisateur une interface simple tout en masquant la complexité sous-jacente.

Le motif Façade améliore presque toujours la facilité d'utilisation d'un logiciel. Reprenant l'exemple de jQuery, l'une des méthodes les plus populaires de la bibliothèque est la prêt() méthode:

$ (document) .ready (function () // tout votre code va ici…);

le prêt() méthode implémente réellement une façade. Si vous regardez la source, voici ce que vous trouverez:

ready: (function () … // Mozilla, Opera et Webkit if (document.addEventListener) document.addEventListener ("DOMContentLoaded", idempotent_fn, false);… // Modèle d'événement IE autrement if (document.attachEvent) // assure le déclenchement avant la charge; peut-être tard, mais sûr aussi pour iframes document.attachEvent ("onreadystatechange", idempotent_fn); // un repli sur window.onload, qui fonctionnera toujours window.attachEvent ("onload", idempotent_fn); …)

Sous le capot, le prêt() La méthode n'est pas si simple. jQuery normalise les incohérences du navigateur pour s'assurer que prêt() est renvoyé au moment opportun. Cependant, en tant que développeur, une interface simple vous est présentée..

La plupart des exemples de motif de façade suivent ce principe. Lors de la mise en œuvre, nous nous basons généralement sur des instructions conditionnelles sous le capot, mais nous les présentons comme une simple interface pour l'utilisateur. D'autres méthodes implémentant ce modèle incluent animer() et css (). Pouvez-vous imaginer pourquoi ils utiliseraient un motif de façade??


Modèles de conception comportementale

Tous les systèmes logiciels orientés objet auront une communication entre les objets. Ne pas organiser cette communication peut conduire à des bugs difficiles à trouver et à corriger. Les modèles de conception comportementaux prescrivent différentes méthodes pour organiser la communication entre les objets. Dans cette section, nous allons examiner les modèles Observer et Mediator..

Modèle d'observateur

Le modèle Observer est le premier des deux modèles de comportement que nous allons traverser. Voici ce qu'il dit:

Dans le modèle d'observateur, un sujet peut avoir une liste d'observateurs intéressés par son cycle de vie. Chaque fois que le sujet fait quelque chose d'intéressant, il envoie une notification à ses observateurs. Si un observateur n'est plus intéressé par l'écoute du sujet, le sujet peut le supprimer de sa liste..

Cela semble assez simple, non? Nous avons besoin de trois méthodes pour décrire ce modèle:

  • publier (données): Appelé par le sujet lorsqu'il a une notification à faire. Certaines données peuvent être transmises par cette méthode.
  • s'inscrire (observateur): Appelé par le sujet à ajouter un observateur à sa liste d'observateurs.
  • désinscription (observateur): Appelé par le sujet à retirer un observateur de sa liste d'observateurs.

Eh bien, il s'avère que les bibliothèques JavaScript les plus modernes prennent en charge ces trois méthodes dans le cadre de leur infrastructure d'événements personnalisée. Habituellement, il y a un sur() ou attacher() méthode, un déclencheur() ou Feu() méthode et un de() ou détacher() méthode. Considérez l'extrait suivant:

// Nous venons de créer une association entre les méthodes d'événements jQuery
// et ceux prescrits par le modèle Observer, mais ce n'est pas obligatoire. var o = $ (); $ .subscribe = o.on.bind (o); $ .unsubscribe = o.off.bind (o); $ .publish = o.trigger.bind (o); // Usage document.on ('tweetsReceived', function (tweets) // effectue certaines actions, puis déclenche un événement $ .publish ('tweetsShow', tweets);); // Nous pouvons nous abonner à cet événement et ensuite déclencher notre propre événement. $ .subscribe ('tweetsShow', function () // affiche les tweets d'une manière ou d'une autre… // publie une action après leur affichage. $ .publish ('tweetsDisplayed);); $ .subscribe ('tweetsDisplayed, function () …);

Le modèle Observer est l’un des modèles les plus simples à implémenter, mais il est très puissant. JavaScript est bien adapté pour adopter ce modèle car il est naturellement basé sur les événements. La prochaine fois que vous développerez des applications Web, envisagez de développer des modules faiblement couplés et adoptez le modèle Observer comme moyen de communication. Le modèle d'observateur peut devenir problématique s'il y a trop de sujets et d'observateurs impliqués. Cela peut arriver dans les systèmes à grande échelle, et le prochain motif que nous examinons tente de résoudre ce problème..

Modèle de médiateur

Le dernier motif que nous allons examiner est le motif Médiateur. Il ressemble au motif Observer mais avec quelques différences notables.

Le modèle Mediator favorise l'utilisation d'un seul sujet partagé qui gère la communication avec plusieurs objets. Tous les objets communiquent les uns avec les autres via le médiateur.

Une bonne analogie avec le monde réel serait une tour de trafic aérien, qui gère la communication entre l'aéroport et les vols. Dans le monde du développement logiciel, le motif Mediator est souvent utilisé car un système devient trop compliqué. En plaçant des médiateurs, la communication peut être gérée via un seul objet, plutôt que d'avoir plusieurs objets en communication les uns avec les autres. Dans ce sens, un motif médiateur peut être utilisé pour remplacer un système qui implémente le motif observateur..

Addy Osmani a implémenté de manière simplifiée le motif Mediator. Parlons de la façon dont vous pouvez l'utiliser. Imaginez que vous ayez une application Web qui permette aux utilisateurs de cliquer sur un album et d'y écouter de la musique. Vous pouvez créer un médiateur comme celui-ci:

$ ('# album'). on ('click', fonction (e) e.preventDefault (); var albumId = $ (this) .id (); mediator.publish ("playAlbum", albumId);) ; var playAlbum = function (id) … mediator.publish ("albumStartedPlaying", songList: […], currentSong: "Sans toi"); ; var logAlbumPlayed = function (id) // Journalise l'album dans le backend; var updateUserInterface = function (album) // Met à jour l'interface utilisateur pour refléter ce qui est en cours de lecture; // abonnements Mediator mediator.subscribe ("playAlbum", playAlbum); mediator.subscribe ("playAlbum", logAlbumPlayed); mediator.subscribe ("albumStartedPlaying", updateUserInterface);

L'avantage de ce modèle par rapport au modèle Observer est qu'un seul objet est responsable de la communication, alors que dans le modèle d'observateur, plusieurs objets peuvent être en écoute et s'abonner l'un à l'autre..

Dans le modèle Observer, il n'y a pas d'objet unique qui encapsule une contrainte. Au lieu de cela, l'observateur et le sujet doivent coopérer pour maintenir la contrainte. Les schémas de communication sont déterminés par la manière dont les observateurs et les sujets sont interconnectés: un sujet unique compte généralement de nombreux observateurs, et parfois l'observateur d'un sujet est le sujet d'un autre observateur..


Conclusion

Quelqu'un l'a déjà appliqué avec succès dans le passé.

La grande chose à propos des modèles de conception est que quelqu'un l'a déjà appliqué avec succès dans le passé. De nombreux codes open source implémentent différents modèles en JavaScript. En tant que développeurs, nous devons savoir quels modèles existent et à quel moment les appliquer. J'espère que ce tutoriel vous a aidé à franchir une étape supplémentaire dans la réponse à ces questions..


Lecture supplémentaire

Une grande partie du contenu de cet article se trouve dans l'excellent livre Learning JavaScript Design Patterns, par Addy Osmani. C'est un livre en ligne qui a été publié gratuitement sous une licence Creative Commons. Le livre couvre abondamment la théorie et la mise en œuvre de nombreux modèles différents, à la fois en JavaScript vanille et dans diverses bibliothèques JS. Je vous encourage à y regarder comme une référence lorsque vous démarrez votre prochain projet.