Quelle est la différence entre include et extend dans Ruby?
J'ai juste la tête autour de la métaprogrammation de Ruby. Les mixin / modules parviennent toujours à me confondre.
- include: mélanges en module spécifié méthodes de les méthodes d'instance dans la classe cible
- étendre: mélanges en module spécifié méthodes de les méthodes de la classe dans la classe cible
Alors, est la différence majeure juste ceci ou est un plus grand dragon qui 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!"
6 réponses
Ce que vous avez dit est correct. Cependant, il ya plus à lui que cela.
Si vous avez une classe Klazz
et module Mod
, y compris les Mod
dans Klazz
donne des instances de Klazz
accès à Mod
's méthodes. Ou vous pouvez étendre Klazz
avec Mod
donner la classe Klazz
l'accès à Mod
's méthodes. Mais vous pouvez également é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 de la même classe que o
ne le font pas.
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 a les méthodes de Mod (comme méthodes de classe) - si vous appelez
obj.extend(Mod)
, maintenant obj a du Mod méthodes (comme les méthodes d'instance), mais aucune autre instance deobj.class
a ces méthodes ajoutées. -
extend
est une méthode publique
Inclure Par défaut, il se mélange dans le module spécifié est méthodes en tant que méthodes d'instance dans le module/classe cible. par exemple
- si vous appelez
class Klazz; include Mod; end;
, toutes les instances de Klazz ont maintenant accès aux méthodes de Mod (en tant que méthodes d'instance) -
include
est une méthode privée, car elle est destinée à être appelée depuis la classe/le module du conteneur.
Cependant, modules très souvent remplacer include
'le comportement de singe de correction de la included
méthode. 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 avez 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
- Il inclut également les constantes de Mod dans Klazz, tant qu'elles ne s'affrontent pas
- Il donne à Klazz l'accès aux variables de module de Mod, par exemple
@@foo
ou@@bar
- soulève ArgumentError s'il y a cyclique comprend
- attache le module comme ancêtre immédiat de L'appelant (c'est-à-dire qu'il ajoute Mod à Klazz.ancêtres, mais Mod n'est pas ajouté à la chaîne de Klazz.superclasse.superclasse.superclasse. Donc, appeler
super
dans Klazz # foo vérifiera Mod # foo avant de vérifier la méthode foo de la vraie superclasse de Klazz. Voir le RubySpec pour plus de détails.).
Bien sûr, ruby documentation est toujours le meilleur endroit où aller pour ces choses. le projet RubySpec était également un ressource fantastique, car ils ont documenté la fonctionnalité précisément.
C'est exact.
Dans les coulisses, include est en fait un alias pour append_features , qui (à partir des documents):
L'implémentation par défaut de Ruby est ajoutez les constantes, les méthodes et le module variables de ce module à un module si ce module n'a pas encore été ajouté pour aModule ou l'un de ses ancêtres.
Toutes les autres réponses sont bonnes, y compris l'astuce pour creuser à travers 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 module réutilisable dans la classe Classthatiincludes, les méthodes, constantes, classes, sous-modules et autres déclarations sont référencées.
Si vous étendez class ClassThatExtends avec le module Réutilisablemodule, alors les méthodes et les constantes sont copiées . Évidemment, si vous ne faites pas attention, vous pouvez gaspiller beaucoup de mémoire en dupliquant dynamiquement les définitions.
Si vous utilisez ActiveSupport:: préoccupation, le .la fonctionnalité included() vous permet de réécrire directement la classe including. module ClassMethods dans une préoccupation obtient extended (copié) dans la classe including.
Je l'ai appris avant mais j'apprécie quand je l'utilise. Voici la différence:
Cela ne fonctionne pas, mais si j'ai défini comme def page_views(campaign)
:
class UserAction
include Calculations
def self.page_views(campaign)
overall_profit = calculate_campaign_profit(campaign)
end
end
Cela fonctionne:
class UserAction
extend Calculations
def self.page_views(campaign)
overall_profit = calculate_campaign_profit(campaign)
end
end
Je voudrais aussi expliquer le mécanisme tel qu'il fonctionne. Si je n'ai pas raison, veuillez corriger.
Lorsque nous utilisons include
nous ajoutons une liaison de notre classe à un module qui contient des méthodes.
class A
include MyMOd
end
a = A.new
a.some_method
Les objets n'ont pas de méthodes, seuls les classes et les modules le font.
Donc, quand a
reçoit mesage {[3] } il commence la méthode de recherche some_method
dans la classe propre de a
, puis dans la classe A
, puis dans les modules de classe liés à A
s'il y en a (dans l'ordre inverse, dernier inclus gagner).
Lorsque nous utilisons extend
nous ajoutons une liaison à un module dans la classe propre de l'objet.
Donc, si nous utilisons A. de nouveaux.extend (MyMod) nous ajoutons un lien à notre module à la classe eigen de l'instance de A ou à la classe a'
.
Et si nous utilisons A. extend (MyMod) nous ajoutons un lien à A (object's, les classes sont aussi des objets) eigenclass A'
.
Ainsi, le chemin de recherche de la méthode pour a
est le suivant:
a = > ' un '= > modules liés à une " classe => A.
Il existe également une méthode prepend qui change la recherche chemin:
A = > A' => modules préfixés A = > a = > module inclus à A
Désolé pour mon mauvais anglais.