Objectifs
- Comprendre le concept de polymorphisme en programmation orientée objet.
- Découvrir le “Duck Typing”, la philosophie de Ruby pour un polymorphisme flexible.
- Écrire des méthodes capables de travailler avec des objets de différents types, tant qu’ils partagent un comportement commun.
Qu’est-ce que le polymorphisme ?
Le mot “polymorphisme” vient du grec et signifie “qui peut prendre plusieurs formes”. En programmation, c’est la capacité d’objets de types différents à répondre au même message (au même appel de méthode), chacun à sa manière.
Exemple avec l’héritage
L’héritage est une manière d’obtenir du polymorphisme.
class Animal
def parler
# Comportement par défaut
end
end
class Chien < Animal
def parler
"Waf waf"
end
end
class Chat < Animal
def parler
"Miaou"
end
end
# Cette méthode peut recevoir n'importe quel animal
def faire_parler(animal)
puts animal.parler
end
faire_parler(Chien.new) # => "Waf waf"
faire_parler(Chat.new) # => "Miaou"
La méthode faire_parler fonctionne avec les deux objets car ils sont tous les deux des Animal et répondent à la méthode parler.
Le “Duck Typing” : La flexibilité de Ruby
Ruby pousse le polymorphisme plus loin avec une philosophie appelée “Duck Typing”.
La phrase célèbre est :
“If it walks like a duck and it quacks like a duck, then it must be a duck.” (S’il marche comme un canard et cancane comme un canard, alors ce doit être un canard.)
En d’autres termes, Ruby ne se soucie pas de la classe d’un objet ou de son héritage. Il se soucie seulement des méthodes auxquelles cet objet peut répondre.
Exemple de Duck Typing
Reprenons notre exemple, mais sans aucun lien d’héritage.
class Canard
def cancaner
"Coin coin !"
end
end
class PersonneEnColere
def cancaner
"Arrêtez de faire du bruit !"
end
end
# Cette méthode ne demande qu'une chose : que l'objet qu'on lui passe
# ait une méthode 'cancaner'. Sa classe n'a aucune importance.
def declencher_son(objet_qui_cancane)
puts objet_qui_cancane.cancaner
end
declencher_son(Canard.new) # => "Coin coin !"
declencher_son(PersonneEnColere.new) # => "Arrêtez de faire du bruit !"
La méthode declencher_son fonctionne pour les deux, car ils se comportent tous les deux comme des “canards” du point de vue de cette méthode (ils répondent à .cancaner).
Pourquoi est-ce si puissant ?
Le Duck Typing favorise l’écriture de code flexible et découplé.
- Vous n’avez pas besoin de créer des hiérarchies de classes complexes.
- Vous pouvez facilement remplacer un objet par un autre (par exemple, pour des tests) tant qu’il respecte la même “interface” (les mêmes noms de méthodes).
La méthode puts en est le parfait exemple. Elle ne se soucie pas de ce que vous lui passez, tant que l’objet a une méthode .to_s pour se transformer en chaîne de caractères. puts 5, puts "hello", puts [1, 2] fonctionnent tous, car Integer, String et Array ont tous une méthode .to_s.
Bonnes pratiques
- Ne vérifiez pas le type, vérifiez le comportement. Évitez d’écrire du code comme
if objet.is_a?(Canard). Appelez simplement la méthode dont vous avez besoin. Si l’objet ne l’a pas, Ruby lèvera une erreurNoMethodError, ce qui est souvent le comportement correct pour signaler qu’on a passé le mauvais type d’objet. - Lorsque vous écrivez des méthodes, pensez en termes de comportements attendus plutôt qu’en termes de classes spécifiques.
Exercices
-
Le concert :
- Créez trois classes :
Guitariste,Pianiste, etBatteur. - Chacune de ces classes doit avoir une méthode
jouer_instrumentqui retourne une chaîne de caractères différente (ex: “Cling cling”, “Pling pling”, “Boum boum”). - Créez une méthode
demarrer_concertqui prend un tableau de musiciens. - À l’intérieur, la méthode doit itérer sur le tableau et appeler
jouer_instrumentsur chaque musicien. - Testez en créant un groupe avec un guitariste et un batteur.
- Créez trois classes :
-
Le Duck Typing avec des fichiers :
- La méthode
File.openretourne un objet de typeFile. On peut écrire dedans avec.puts. - Créez une classe
ConsoleLoggerqui a aussi une méthodeputsqui affiche simplement le message dans le terminal avec un préfixe “LOG: ”. - Créez une méthode
enregistrer_message(destination, message)qui appelledestination.puts(message). - Montrez que vous pouvez appeler
enregistrer_messageavec un vrai fichier ouvert, mais aussi avec une instance de votreConsoleLogger.
- La méthode