Retour au cours

polymorphisme et duck typing

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 erreur NoMethodError, 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

  1. Le concert :

    • Créez trois classes : Guitariste, Pianiste, et Batteur.
    • Chacune de ces classes doit avoir une méthode jouer_instrument qui retourne une chaîne de caractères différente (ex: “Cling cling”, “Pling pling”, “Boum boum”).
    • Créez une méthode demarrer_concert qui prend un tableau de musiciens.
    • À l’intérieur, la méthode doit itérer sur le tableau et appeler jouer_instrument sur chaque musicien.
    • Testez en créant un groupe avec un guitariste et un batteur.
  2. Le Duck Typing avec des fichiers :

    • La méthode File.open retourne un objet de type File. On peut écrire dedans avec .puts.
    • Créez une classe ConsoleLogger qui a aussi une méthode puts qui affiche simplement le message dans le terminal avec un préfixe “LOG: ”.
    • Créez une méthode enregistrer_message(destination, message) qui appelle destination.puts(message).
    • Montrez que vous pouvez appeler enregistrer_message avec un vrai fichier ouvert, mais aussi avec une instance de votre ConsoleLogger.