Je me concentre sur la métaprogrammation Ruby. Les mixin / modules parviennent toujours à me confondre.
- include : mélange dans les méthodes de module spécifiées en tant que méthodes d' instance dans la classe cible
- extend : mélange dans les méthodes de module spécifiées en tant que méthodes de classe dans la classe cible
Alors, la différence principale est-elle juste ou est-ce qu'un plus gros 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!"
Réponses:
Ce que vous avez dit est correct. Mais il y a plus que cela.
Si vous avez une classe
Klazz
et un moduleMod
, y comprisMod
dansKlazz
donne des instances d'Klazz
accès auxMod
méthodes de. Ou vous pouvez étendreKlazz
enMod
donnant à la classe l'Klazz
accès auxMod
méthodes de. Mais vous pouvez également étendre un objet arbitraire aveco.extend Mod
. Dans ce cas, l'objet individuel obtientMod
les méthodes de même si tous les autres objets de la même classeo
n'en ont pas.la source
extend - ajoute les méthodes et les constantes du module spécifié à la métaclasse cible (c'est-à-dire la classe singleton), par exemple
Klazz.extend(Mod)
, Klazz a maintenant les méthodes de Mod (comme méthodes de classe)obj.extend(Mod)
, obj a maintenant les méthodes de Mod (comme méthodes d'instance), mais aucune autre instance de ofobj.class
n'a ajouté ces méthodes.extend
est une méthode publiqueinclude - Par défaut, il se mélange dans les méthodes du module spécifié en tant que méthodes d'instance dans le module / classe cible. par exemple
class Klazz; include Mod; end;
, maintenant toutes les instances de Klazz ont 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 à partir de la classe / module conteneur.Cependant , les modules remplacent très souvent
include
le comportement de 'en corrigeant laincluded
méthode par des singes . 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@@foo
ou@@bar
super
Klazz # foo vérifiera Mod # foo avant de vérifier à la méthode foo de la véritable superclasse de Klazz. Voir la RubySpec pour plus de détails.).Bien sûr, la documentation de base ruby est toujours le meilleur endroit où aller pour ces choses. Le projet RubySpec était également une ressource fantastique, car ils documentaient précisément la fonctionnalité.
#include
RubySpec rubydoc#included
RubySpec rubydoc#extend
RubySpec rubydoc#extended
RubySpec rubydoc#extend_object
RubySpec rubydoc#append_features
RubySpec rubydocla source
extend
appliquer des méthodes en tant que méthodes de classe ou d' instance, selon l'utilisation.Klass.extend
= méthodes de classe,objekt.extend
= méthodes d'instance. J'ai toujours (à tort) supposé que les méthodes de classe provenaientextend
et que l'instance provenait deinclude
.C'est correct.
Dans les coulisses, include est en fait un alias pour append_features , qui (à partir des documents):
la source
Lorsque vous
include
un module dans une classe, les méthodes du module sont importées en tant que méthodes d'instance .Cependant, lorsque vous
extend
insérez un module dans une classe, les méthodes du module sont importées en tant que méthodes de classe .Par exemple, si nous avons un module
Module_test
défini comme suit:Maintenant, pour le
include
module. Si nous définissons la classeA
comme suit:La sortie sera:
M - in module
.Si nous remplaçons la ligne
include Module_test
avecextend Module_test
et exécuter le code à nouveau, nous recevons l'erreur suivante:undefined method 'func' for #<A:instance_num> (NoMethodError)
.Modification de l'appel de méthode
a.func
pourA.func
, la sortie passe à:M - in module
.De l'exécution du code ci-dessus, il est clair que lorsque nous
include
un module, ses méthodes deviennent des méthodes d'instance et quand nousextend
un module, ses méthodes deviennent des méthodes de classe .la source
Toutes les autres réponses sont bonnes, y compris l'astuce pour fouiller dans 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
Quant aux cas d'utilisation:
Si vous incluez le module ReusableModule dans la classe ClassThatIncludes, les méthodes, constantes, classes, sous-modules et autres déclarations sont référencées.
Si vous prolongez ClassThatExtends de classe avec le module ReusableModule, puis les méthodes et les constantes obtient 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 :: Concern, la fonctionnalité .included () vous permet de réécrire directement la classe incluse. Le module ClassMethods à l'intérieur d'une préoccupation est étendu (copié) dans la classe incluse.
la source
Je voudrais également expliquer le mécanisme tel qu'il fonctionne. Si je n'ai pas raison, veuillez corriger.
Lorsque nous utilisons,
include
nous ajoutons un lien de notre classe à un module qui contient certaines méthodes.Les objets n'ont pas de méthodes, seuls les clases et les modules en ont. Ainsi, lorsque
a
reçoit un message,some_method
il commence la méthode de recherchesome_method
dansa
la classe propre de, puis dans laA
classe et ensuite dansA
les modules de classe s'il y en a (dans l'ordre inverse, les dernières victoires incluses).Lorsque nous utilisons,
extend
nous ajoutons une liaison à un module dans la classe propre de l'objet. Donc, si nous utilisons A.new.extend (MyMod), nous ajoutons un lien à notre module à la classe ou à laa'
classe propre de l'instance de A. Et si nous utilisons A.extend (MyMod), nous ajoutons une liaison à la classe propre A (les objets, les classes sont également des objets)A'
.le chemin de recherche de méthode
a
est donc le suivant: a => a '=> modules liés à une' class => A.il existe également une méthode de pré-ajout qui modifie le chemin de recherche:
a => a '=> modules ajoutés à A => A => module inclus à A
Désolé pour mon mauvais anglais.
la source