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!"
364
demandé sur nbro 2008-10-01 09:40:58

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.

221
répondu domgblackwell 2016-03-07 02:32:47

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 de obj.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.

290
répondu John Douthat 2015-12-22 21:53:23

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.

13
répondu Toby Hede 2008-10-01 08:08:30

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.

4
répondu Ho-Sheng Hsiao 2011-09-22 21:02:06

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
1
répondu Caner Çakmak 2015-07-02 12:21:13

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.

1
répondu user1136228 2016-03-27 12:11:02