Je me contente de ma tête Ruby métaprogrammation. Les mixin/modules parviennent toujours à m'embrouiller.
La différence principale est-elle simplement celle-ci ou est-ce qu'un plus grand dragon se cache? .
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!"
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 aux instances de Klazz
accès aux méthodes de Mod
. Ou vous pouvez étendre Klazz
avec Mod
en donnant à classeKlazz
l'accès aux méthodes de Mod
. Mais vous pouvez aussi étendre un objet arbitraire avec o.extend Mod
. Dans ce cas, l'objet individuel obtient les méthodes de Mod
alors que tous les autres objets de la même classe que o
n'en ont 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.
Klazz.extend(Mod)
, Klazz utilise maintenant les méthodes de Mod (comme méthodes de classe)obj.extend(Mod)
, obj utilise maintenant les méthodes de Mod (comme méthodes d'instance), mais aucune autre instance de obj.class
n'a ces méthodes ajoutées.extend
est une méthode publiqueinclude - 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.
class Klazz; include Mod; end;
, toutes les instances de Klazz ont désormais 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 à partir de la classe/du module conteneur. Cependant , les modules très souvent overrideinclude
se comporte en appliquant un correctif à la méthode included
. 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
@@foo
ou @@bar
super
dans Klazz # foo vérifie Mod # foo auparavant. vérifiant la méthode foo de la super classe de la classe principale (voir la RubySpec pour plus de détails).Bien sûr, la documentation Ruby de base est toujours le meilleur endroit pour ces choses. Le projet RubySpec était également une ressource fantastique, car ils documentaient précisément les fonctionnalités.
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 consiste à ajouter les constantes, méthodes et variables de module de ce module à aModule si ce module n'a pas déjà été ajouté à aModule ou à l'un de ses ancêtres.
Toutes les autres réponses sont bonnes, y compris le conseil de Dig à 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
En ce qui concerne 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. Le 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-le.
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, seulement des classes et des modules. Ainsi, lorsque a
reçoit un message some_method
, il commence la méthode de recherche some_method
dans la classe propre de a
, puis dans la classe A
puis liée à A
. modules de classe s’il y en a (dans l’ordre inverse, les derniers gains inclus).
Lorsque nous utilisons extend
, nous ajoutons des liens à 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 un lien à A (les objets, les classes sont aussi des objets) eigenclass A'
.
ainsi, le chemin de recherche de méthode pour a
est le suivant: a => a '=> modules liés à une' classe => A.
il existe également une méthode prepend qui modifie le chemin de recherche:
a => a '=> module ajouté à A => A => module inclus à A
désolé pour mon mauvais anglais.