web-dev-qa-db-fra.com

Quelle est la difference entre include et extend in Ruby?

Je me contente de ma tête Ruby métaprogrammation. Les mixin/modules parviennent toujours à m'embrouiller.

  • 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

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!"
392
Gishu

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.

238
domgblackwell

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), Klazz utilise maintenant les méthodes de Mod (comme méthodes de classe)
  • si vous appelez 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 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.

  • si vous appelez 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
  • 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 se contredisent pas
  • Il donne à Klazz l’accès aux variables de module de Mod, par exemple. @@foo ou @@bar
  • déclenche ArgumentError s'il existe des inclusions cycliques
  • Associe le module en tant qu’ancêtre immédiat de l’appelant (c’est-à-dire qu’il ajoute Mod à Klazz.ancestors, mais Mod n’est pas ajouté à la chaîne de Klazz.superclass.superclass.superclass. C’est pourquoi appeler 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.

308
John Douthat

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.

14
Toby Hede

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.

3
Ho-Sheng Hsiao

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.

1
user1136228