Quelle est la difference entre include et extend in Ruby?
Question
Je commence juste à comprendre la métaprogrammation de Ruby. Les mixin / modules parviennent toujours à m'embrouiller.
- include : mélange dans les méthodes de module spécifiées en tant que méthodes d'instance dans la classe cible
- étendre : mélange les méthodes de module spécifiées en tant que méthodes de classe dans la classe cible
Alors, est-ce que la principale différence est-ce juste ou est-ce qu'un plus grand dragon se cache? par exemple
module ReusableModule
def module_method
puts "Module Method: Hi there!"
end
end
class ClassThatIncludes
include ReusableModule
end
class ClassThatExtends
extend ReusableModule
end
puts "Include"
ClassThatIncludes.new.module_method # "Module Method: Hi there!"
puts "Extend"
ClassThatExtends.module_method # "Module Method: Hi there!"
La solution
Ce que vous avez dit est correct. Cependant, il y a plus que cela.
Si vous avez une classe Klazz
et un module Mod
, y compris Mod
dans Klazz
donne des instances de Klazz
accès aux méthodes de Mod
. Ou vous pouvez étendre Klazz
avec Mod
en donnant à la classe Klazz
l'accès à Mod
' s méthodes. Mais vous pouvez aussi étendre un objet arbitraire avec o.extend Mod
. Dans ce cas, l'objet individuel obtient les méthodes de Mod
même si tous les autres objets ayant la même classe que o
ne le font pas.
Autres conseils
extend : ajoute les méthodes et les constantes du module spécifié à la métaclasse de la cible (c'est-à-dire la classe singleton). par exemple.
- si vous appelez
Klazz.extend (Mod)
, maintenant Klazz utilise les méthodes de Mod (méthodes de classe) - si vous appelez
obj.extend (Mod)
, obj a maintenant les méthodes de Mod (comme méthodes d'instance), mais aucune autre instance deobj.class
n'a ces méthodes ajouté. -
extend
est une méthode publique
include : par défaut, il mélange les méthodes du module spécifié en tant que méthodes d'instance dans le module / la classe cible. par exemple
- si vous appelez
class Klazz; inclure Mod; fin;
, toutes les instances de Klazz ont maintenant accès aux méthodes de Mod (comme méthodes d’instance) -
include
est une méthode privée, car elle est destinée à être appelée depuis le conteneur / la classe de conteneur.
Cependant, , les modules remplacent très souvent le comportement de include
en masquant la méthode avec
. Ceci est très important dans le code Rails hérité. plus de détails de Yehuda Katz .
Plus de détails sur include
, avec son comportement par défaut, en supposant que vous ayez exécuté le code suivant
class Klazz
include Mod
end
- Si Mod est déjà inclus dans Klazz ou l'un de ses ancêtres, l'instruction include n'a aucun effet
- Cela inclut également les constantes de Mod dans Klazz, tant qu'elles ne se contredisent pas
- Cela donne à Klazz l’accès aux variables de module de Mod, par exemple.
@@ foo
ou@@ bar
- déclenche ArgumentError s'il existe des inclusions cycliques
- Associe le module en tant qu’ancêtre immédiat de l’appelant (c’est-à-dire qu’il ajoute Mod à Klazz.ancestors, mais Mod n’est pas ajouté à la chaîne de Klazz.superclass.superclass.superclass. Donc, appeler
super
dans Klazz # foo va vérifier Mod # foo avant de vérifier la méthode foo de la super classe de Klazz (voir la RubySpec pour plus de détails).
Bien entendu, la la documentation Ruby Core est toujours le meilleur endroit pour aller pour ces choses. Le le projet RubySpec était également une ressource fantastique, car il documentait les fonctionnalités avec précision.
C'est correct.
Dans les coulisses, include est en fait un alias pour append_features , qui (d'après la documentation):
L'implémentation par défaut de Ruby est de ajouter les constantes, les méthodes et le module les variables de ce module à aModule si ce module n'a pas encore été ajouté à un module ou à l'un de ses ancêtres.
Toutes les autres réponses sont bonnes, y compris le conseil pour explorer RubySpecs:
https://github.com/rubyspec/rubyspec/ blob / master / core / module / include_spec.rb
https://github.com/rubyspec/rubyspec/ blob / master / core / module / extend_object_spec.rb
Comme pour les cas d'utilisation:
Si vous incluez le module ReusableModule dans la classe ClassThatIncludes, les méthodes, les constantes, les classes, les sous-modules et les autres déclarations sont référencés.
Si vous étendez la classe ClassThatExtends avec le module ReusableModule, les méthodes et les constantes sont alors copiées . Évidemment, si vous ne faites pas attention, vous pouvez gaspiller beaucoup de mémoire en dupliquant de manière dynamique les définitions.
Si vous utilisez ActiveSupport :: Concern, la fonctionnalité .included () vous permet de réécrire directement la classe incluse. module ClassMethods à l'intérieur d'une préoccupation est étendu (copié) dans la classe incluse.
Je voudrais aussi expliquer le mécanisme tel qu'il fonctionne. Si je ne me trompe pas, corrigez.
Lorsque nous utilisons include
, nous ajoutons un lien de notre classe à un module contenant des méthodes.
class A
include MyMOd
end
a = A.new
a.some_method
Les objets n'ont pas de méthodes, mais seulement des classes et des modules.
Ainsi, lorsque a
reçoit le message some_method
, il commence la méthode de recherche some_method
dans une classe propre
, puis dans Une classe
puis en liaison avec les modules de classe A
s'il y en a (dans l'ordre inverse, les dernières victoires incluses).
Lorsque nous utilisons extend
, nous ajoutons un lien à un module de la classe propre de l'objet.
Donc, si nous utilisons A.new.extend (MyMod), nous ajoutons un lien vers notre module à la classe propre de l'instance de A ou à la classe a '
.
Et si nous utilisons A.extend (MyMod), nous ajoutons des liens à A (les objets, les classes sont aussi des objets) eigenclass A '
.
so chemin de recherche de méthode pour a
est le suivant:
a = > a '= > modules liés à un 'class = > A.
Il existe également une méthode de préposition qui modifie le chemin de recherche:
a = > a '= > modules préfabriqués A = > A = > module inclus à A
désolé pour mon mauvais anglais.