Objectifs
- Comprendre les deux rôles principaux d’un module en Ruby.
- Utiliser un module comme namespace pour organiser son code et éviter les conflits de noms.
- Utiliser un module comme mixin pour partager des fonctionnalités communes entre plusieurs classes.
Un module est une collection de méthodes, de constantes et d’autres modules ou classes. On ne peut pas créer d’instance d’un module (pas de MonModule.new). Ils servent à deux choses principales.
1. Utilisation comme “Namespace” (Espace de nommage)
C’est l’usage le plus simple. Un module peut servir de “boîte” ou de “dossier” pour regrouper des classes et des méthodes qui ont un lien logique. Cela permet d’éviter les conflits si deux classes ont le même nom dans des parties différentes d’une grosse application.
module ECommerce
class Commande
# ... logique de commande
end
end
module SupportClient
class Commande
# ... logique de commande de support, complètement différente
end
end
# Pour utiliser ces classes, on les préfixe avec le nom du module
nouvelle_commande = ECommerce::Commande.new
ticket_support = SupportClient::Commande.new
L’opérateur :: est l’opérateur de résolution de portée.
2. Utilisation comme “Mixin” (Partage de comportement)
C’est l’usage le plus puissant et le plus idiomatique de Ruby. Un mixin permet d’injecter des méthodes d’un module directement dans une classe. C’est la solution de Ruby pour le partage de code, qui est souvent préférée à l’héritage multiple (qui n’existe pas en Ruby).
On utilise le mot-clé include dans une classe pour “mixer” un module. Toutes les méthodes définies dans le module deviennent alors des méthodes d’instance de la classe.
Exemple de Mixin
Imaginons que nous ayons plusieurs classes qui ont besoin d’une fonctionnalité de “déplacement”.
# On définit le comportement dans un module
module Deplacable
def avancer
puts "J'avance."
end
def reculer
puts "Je recule."
end
end
# Maintenant, on peut inclure ce module dans n'importe quelle classe
class Voiture
include Deplacable
# La classe Voiture a maintenant les méthodes 'avancer' et 'reculer'
end
class Robot
include Deplacable
# Le Robot aussi !
end
ma_voiture = Voiture.new
mon_robot = Robot.new
ma_voiture.avancer # => "J'avance."
mon_robot.reculer # => "Je recule."
Cette approche est extrêmement flexible. Une classe peut inclure plusieurs modules pour hériter de plusieurs comportements différents.
include vs extend
include MonModule: Les méthodes du module deviennent des méthodes d’instance. C’est ce que vous utiliserez le plus souvent.extend MonModule: Les méthodes du module deviennent des méthodes de classe.
module MonHelper
def methode_utile
"Ceci est utile"
end
end
class MaClasse
extend MonHelper
end
# La méthode est sur la classe, pas sur l'instance
MaClasse.methode_utile # => "Ceci est utile"
Bonnes pratiques
- Utilisez des modules pour regrouper des fonctionnalités qui ont un lien sémantique (ex:
Paiement,Authentification). - Utilisez les mixins pour partager des comportements transverses qui ne sont pas liés par une relation “est un” (héritage). Par exemple, un module
Loggablequi fournit une méthodelogpeut être inclus dans n’importe quelle classe qui a besoin d’écrire des logs.
Exercices
-
Module de calcul :
- Créez un module
MathUtilsqui contient deux méthodes :doubler(nombre)ettripler(nombre). - Créez une classe
Calculatricequi n’a pas de méthodes propres. - Incluez le module dans la classe.
- Créez une instance de
Calculatriceet appelez les méthodesdoublerettripler.
- Créez un module
-
Mixin de communication :
- Créez un module
Communicationavec une méthodedire_bonjouret une méthodedire_au_revoir. - Créez deux classes,
HumainetAnimalDeCompagnie. - Faites en sorte que la classe
Humainpuisse utiliser les deux méthodes. - Faites en sorte que la classe
AnimalDeCompagniene puisse utiliser que la méthodedire_bonjour. (Indice : vous pouvez définir un autre module qui n’inclut qu’une partie du premier).
- Créez un module
-
Namespace :
- Créez un module
Inventairecontenant une classeProduit. - Créez un module
Marketingcontenant également une classeProduit. - Instanciez un objet de chaque classe pour montrer qu’il n’y a pas de conflit.
- Créez un module