Journal de Diatomée
Art · Éthique · Logiciel
À propos · Photos illustrées de vers
12 personnages, 12 illustrations (partie 2/2) · 12 personnages, 12 illustrations (partie 1/2) · ·

Un peu de POO en Javascript

Publié le 07/01/2018 dans Logiciel.

Ceci constitue un petit récapitulatif de ce que je viens d’apprendre en programmation orientée objet (POO), pour ce qui concerne le langage Javascript (ES6).

Composition plutôt qu’héritage

Tout d’abord, il est préférable de penser « composition » plutôt que « héritage » pour l’établissement de liens entre objets. On peut dire qu’un chien est un animal et être tenté d’avoir une classe Animal et une classe Chien. La seconde hériterait des propriétés et méthodes de la première, puisqu’un chien est un animal. Cependant, que se passe t-il, si plus tard, il existe des chiens-robots ? Il faudra créer une nouvelle classe qui fera quelque peu doublon avec le code d’origine ou enlever l’héritage et refaire une bonne partie du code. Avec la composition, un objet reçoit des fonctions : un chien pourra naître, aboyer et manger tandis qu’un chien-robot pourra aboyer et jouer. C’est plus simple à penser, surtout au début du développement de l’application et aucune évolution non-prévue ne sera problématique à instancier.

Pour bien comprendre et voir la mise en oeuvre de la composition, je te recommande l’article Composition over Inheritance de Mattias Petter Johansson.

Lien de composition

Le mot « composition » a ici un autre sens que dans la section précédente. Voir cette relation au sein d’un modèle permet d’organiser de façon optimale le code. C’est une relation que j’utilise beaucoup dans mes développements. Dans Narval (le générateur de blog), un article se compose de métadonnées et d’un contenu et si l’article disparaît, les métadonnées et le contenu disparaissent également. Pareil pour Mountale (le générateur de texte bien formaté) ou l’application comporte des documents qui comportent des calques qui comportent des blocs.

Attention : La composition est différente de l’agrégation. Par exemple, un courriel peut se composer de pièces jointes, mais si on supprime le courriel, la pièce existe toujours (même si elle n’est plus jointe). C’est donc une agrégation.

En Javascript, pour traduire la composition, on aura quelque chose du genre :

class Application {
	constructor {
		this.projects = […] // liste d’instances de la classe Project
		…

Ce site donne de très bonnes informations sur la POO, bien que ce soit destiné au langage C#. C’est ici que j’ai trouvé ce terme de « composition ». Je le confondais auparavant avec l’héritage…

Des classes et des fabriques

ES6 permet désormais l’utilisation aisée des classes en Javascript, comme dans tout bon langage de programmation. On a ainsi droit au mot clef class, au constructor, aux méthodes static, aux méthodes d’instances, aux « getters » et aux « setters ». Voyons tout cela en un seul bloc de code :

class Livre {
	constructor(pages) {
		this._pages = pages
		this._pageCourante = 1
	}
	static sommesDesPages(livres) {
		let nb = 0
		for (let l of livres) nb += l.pages
		return nb
	}
	tournerPage() {
		this._pageCourante += 1
	}
	set pages(pages) {
		this._pages = pages
	}
	get pages() {
		return this._pages
	}
}

Une méthode statique permet d’agir sur la classe et non sur une instance de la classe. Il est donc inutile d’employer le mot clef this pour tenter d’accéder aux propriétés d’une instance. Cette méthode s’utilise ainsi : Livre.sommeDesPages([livre2, livre3, livre5]).

tournerPage() est une méthode dite de « prototype ». Ce type de méthode n’accepte aucun argument, mais contrairement aux méthodes statiques, le mot clef this permet d’accéder aux propriétés. Elle s’utilise ainsi : monLivre.tournerPage().

Dans la méthode statique qui calcule le nombre total de pages de la liste de livres mise en argument, j’utilise l.pages plutôt que l._pages. Cela est possible grâce au « getter » pages() qui est appelé automatiquement. Cela fonctionne dans la classe, mais également en dehors. Les propriétés d’un livre sont considérées comme privées lorsque le symbole _ précède leur nom et on y accède via des « getters » et des « setters », dans lesquels il est préférable de mettre des contrôles, puisque le but est de ne pas associer n’importe quelle donnée à n’importe quelle variable. monLivre.pages = [new Page()] utilise automatiquement le setter du nom, mis en place dans la classe.

Facile ! Maintenant, si l’application n’est pas prévue pour contenir plus de 10000 livres, il est recommandé de préférer une fabrique (« factory » en anglais) à une classe, car les performances seraient toujours bonnes (on y verrait que du feu sous les 10000 instances) et que le code serait plus lisible et concis. Plus de mot clef this qui cible parfois ce qu’on ne veut pas une fois hors de la classe et plus de mot clef new pour instancier (monLivre = new Livre(pages)).

Les fabriques sont de simple fonctions qui, comme les classes, génèrent des objets. Reprenons la classe « Livre » pour la convertir en fabrique (sans les méthodes statiques pour simplifier) :

const livre = (pages) => {
	let
		pages = pages,
		pageCourante = 1
	return {
		tournerPage: () => pageCourante += 1
	}
}
const appelCthulhu = livre(lesPages)
appelCthulhu.tournerPage()
console.log(appelCthulhu.pageCourante) // 2

Pratique non ? Il semble que l’on puisse toujours inclure des « getters » et des « setters » puisqu’il ne s’agit pas de quelque chose de réservé aux classes. Quant aux méthodes statiques, elles peuvent être remplacées par de simples fonctions. Je n’ai pas encore fait d’essais, mais ça va venir puisque je reprends le développement de Mountale. Si je trouve des erreurs dans mes exemples ci-dessus, je les corrigerais. J’ai découvert les « factories » grâce à l’article Factory functions in JavaScript, encore de Mattias (que je remercie).