Je me concentre sur la métaprogrammation Ruby. Les mixin / modules parviennent toujours à me confondre.
Donc, la différence majeure est-elle juste ou un dragon plus grand se cache-t-il? 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!"
Ce que vous avez dit est correct. Cependant, il y a plus que cela.
Si vous avez une classe Klazz
et que le module Mod
, y compris Mod
dans Klazz
des exemples d’access de Klazz
aux méthodes de Mod
. Ou vous pouvez étendre Klazz
avec Mod
donnant access à la classe Klazz
aux méthodes de Mod
. Mais vous pouvez également étendre un object arbitraire avec o.extend Mod
. Dans ce cas, l’object individuel obtient les méthodes de Mod
même si tous les autres objects de la même classe ne 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
Klazz.extend(Mod)
, maintenant Klazz a 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 obj.class
n’a ajouté ces méthodes. 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
class Klazz; include Mod; end;
class Klazz; include Mod; end;
, maintenant toutes les instances de Klazz ont access 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 conteneur. Cependant , les modules remplacent très souvent le include
de include
en corrigeant 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 avez exécuté le code suivant
class Klazz include Mod end
@@foo
ou @@bar
super
dans Klazz # foo vérifiera Mod # foo avant vérifier la méthode foo de la super-classe de Klazz (voir RubySpec pour plus de détails). Bien sûr, la documentation de base Ruby est toujours le meilleur endroit pour ces choses. Le projet RubySpec était également une ressource fantastique, car ils ont documenté les fonctionnalités avec précision.
#include
RubySpec rubydoc #extend
RubySpec rubydoc #extended
RubisSpec Rubydoc étendu #extend_object
RubySpec rubydoc #append_features
RubySpec rubydoc C’est correct.
En coulisses, include est en fait un alias pour append_features , qui (à partir des docs):
L’implémentation par défaut de Ruby consiste à append 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 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 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 étendez la classe ClassThatExtends avec le module ReusableModule, 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 dynamicment les définitions.
Si vous utilisez ActiveSupport :: Concern, la fonctionnalité .included () vous permet de réécrire directement la classe d’inclusion. Le module ClassMethods dans une préoccupation est étendu (copié) dans la classe d’inclusion.
Je l’ai appris avant mais je l’apprécie quand je l’utilise. Voici la différence:
Cela ne fonctionne pas mais fonctionnerait si je l’avais 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 marche:
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 un lien de notre classe à un module qui contient certaines méthodes.
class A include MyMOd end a = A.new a.some_method
Les objects n’ont pas de méthodes, seules les classes et les modules le font. Ainsi, lorsqu’un message reçoit a
méthode, il some_method
a
méthode de recherche dans a
classe propre, puis dans A
classe, puis dans A
classe liée à des modules de classe A
s’il y en a (dans l’ordre inverse, les derniers gains inclus).
Lorsque nous utilisons extend
nous ajoutons un lien à un module dans la classe propre de l’object. Donc, si nous utilisons A.new.extend (MyMod), nous ajoutons une liaison à notre module avec la classe eigen de l’instance A ou a'
classe a'
. Et si nous utilisons A.extend (MyMod), nous ajoutons un lien à A (les objects, les classes sont aussi des objects).
Ainsi, le chemin de recherche de méthode pour a
est le suivant: a => a ‘=> modules liés à une’ classe => A.
il y a aussi une méthode prépend qui change le chemin de recherche:
a => a ‘=> modulesto préfixé A => A => module inclus à A
Désolé pour mon mauvais anglais.