Ruby fournit une construction appelée Module
qui est un peu comme une classe mince. Au début, les développeurs Ruby pourraient regarder les modules comme un « tiroir indésirable » de code qui vient d’être jeté et utilisé pour organiser les fichiers, mais il existe une méthode à cette folie. La classe Ruby Class
hérite de Module
et ajoute des choses comme l’instanciation, les propriétés, etc. – tout ce que vous pensez normalement qu’une classe aurait. Parce que Module
est littéralement un ancêtre de Class
, cela signifie que les modules peuvent être traités comme des classes à certains égards.
Le plus important à comprendre est le fonctionnement de la chaîne d’ancêtres Ruby. Comme tous les langages OOP, Ruby prend en charge l’héritage. Lorsque vous héritez d’une classe, la superclasse est ajoutée à votre chaîne d’ancêtres. Vous pouvez afficher les ancêtres de n’importe quelle classe en appelant ancestors
dessus:
class Foo < BarendFoo.ancestors#=>
Comme mentionné, vous pouvez trouver Module
dans le tableau Class.ancestors
.
Lorsque vous include
un module dans votre classe, le module est ajouté à la chaîne d’ancêtres de votre classe – tout comme une classe. Cela fait de include
juste une forme d’héritage, il n’y a rien de spécial à ce sujet.
module Barendclass Foo include BarendFoo.ancestors#=>
Toutes les méthodes de Bar
sont ajoutées à Foo
en tant que méthodes d’instance. Parce que Bar est dans la chaîne, vous pouvez même appeler super
à partir de celle-ci pour appeler des méthodes au-dessus de celle-ci dans la chaîne, qu’elles soient définies sur des modules ou des classes.
class Baz def hello p 'world' endendmodule Bar def hello p 'hello' super endendclass Foo < Baz include BarendFoo.new.hello#=> hello world
Même si Bar
est un module, super
appelle toujours la chaîne et donc Baz#hello
est également appelé. Il est à noter que Bar
est ajouté à la chaîne d’ancêtres devant Baz. Lorsqu’un module est inclus, il est toujours ajouté directement au-dessus de sa classe d’inclusion. Cela peut être déroutant lors de l’ajout de plusieurs modules, car ils sont inclus dans l’ordre « inverse:
class Foo include A include BendFoo.ancestors#=>
Lorsque A
est inclus, il est inséré directement au-dessus de Foo
. Mais lorsque B
est inclus, il est également inséré directement au-dessus de Foo
, il finit donc par atterrir avant A
.
include vs extend
include
est assez facile à comprendre, il ajoute les méthodes du module en tant que méthodes d’instance à sa classe d’inclusion. Vous pouvez penser à extend
en faisant la même chose, mais en ajoutant les méthodes en tant que méthodes de classe.
module Bar def hello p 'hello' endendclass Foo extend BarendFoo.hello # no .new!# => 'hello'
prepend
En plus d’inclure/étendre, Ruby 2.0+ ajoute la méthode prepend
prepend
est similaire à include
, mais insère à la place le module avant la classe d’inclusion dans la chaîne d’héritage.
class Foo include Bar prepend BazendFoo.ancestors#=>
Dans Ruby, vous pouvez rouvrir n’importe quelle classe et redéfinir les méthodes dessus – il ne semble pas utile de remplacer les méthodes d’une classe en utilisant un module ajouté au début plutôt que de les redéfinir. Où prepend
est pratique, c’est lorsque vous avez toujours besoin de l’implémentation originale de la méthode, car les modules ajoutés au début peuvent remplacer les méthodes mais utilisent toujours super
pour appeler l’implémentation d’origine.