Qual'è la differenza tra include ed estende in Ruby?
Domanda
Mi sto solo concentrando sulla metaprogrammazione di Ruby. I mixin / moduli riescono sempre a confondermi.
- includi : mescola i metodi del modulo specificati come metodi di istanza nella classe target
- estende : mescola i metodi del modulo specificato come metodi della classe nella classe target
Quindi la differenza principale è solo questo o è in agguato un drago più grande? per es.
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!"
Soluzione
Quello che hai detto è corretto. Tuttavia, c'è di più.
Se hai una classe Klazz
e il modulo Mod
, incluso Mod
in Klazz
, fornisce istanze di Klazz
accesso ai metodi di Mod
. Oppure puoi estendere Klazz
con Mod
dando alla classe Klazz
l'accesso a Mod
' metodi di s. Ma puoi anche estendere un oggetto arbitrario con o.eendend Mod
. In questo caso il singolo oggetto ottiene i metodi di Mod
anche se non tutti gli altri oggetti con la stessa classe di o
.
Altri suggerimenti
estende : aggiunge i metodi e le costanti del modulo specificato alla metaclasse del target (ovvero la classe singleton) per esempio.
- se chiami
Klazz.extend (Mod)
, ora Klazz ha i metodi Mod (come metodi di classe) - se chiami
obj.extend (Mod)
, ora obj ha i metodi di Mod (come metodi di istanza), ma nessun'altra istanza diobj.class
ha questi metodi aggiunto. -
estende
è un metodo pubblico
include : per impostazione predefinita, mescola i metodi del modulo specificato come metodi di istanza nel modulo / classe di destinazione. per es.
- se chiami
classe Klazz; include Mod; end;
, ora tutte le istanze di Klazz hanno accesso ai metodi di Mod (come metodi di istanza) -
include
è un metodo privato, poiché deve essere chiamato dall'interno della classe / modulo contenitore.
Tuttavia , i moduli molto spesso sovrascrivono includono il comportamento di
correggendo le scimmie con il metodo incluso
. Questo è molto importante nel codice Rails legacy. maggiori dettagli da Yehuda Katz .
Ulteriori dettagli su includono
, con il suo comportamento predefinito, supponendo che tu abbia eseguito il seguente codice
class Klazz
include Mod
end
- Se Mod è già inclusa in Klazz o in uno dei suoi antenati, la dichiarazione include non ha alcun effetto
- Include anche le costanti di Mod in Klazz, purché non si scontrino
- Dà a Klazz l'accesso alle variabili del modulo Mod, ad es.
@@ foo
o@@bar
- genera ArgumentError se ci sono inclusioni cicliche
- Collega il modulo come antenato immediato del chiamante (ovvero aggiunge Mod a Klazz.ancestors, ma Mod non viene aggiunto alla catena di Klazz.superclass.superclass.superclass. Quindi, chiamando
super
in Klazz # foo controllerà Mod # foo prima di verificare il vero metodo foo della superclasse di Klazz. Vedi RubySpec per i dettagli.).
Naturalmente, la documentazione di base su ruby ?? è sempre il posto migliore dove andare per queste cose. Anche il Il progetto RubySpec era anche una risorsa fantastica, perché documentavano con precisione la funzionalità.
È corretto.
Dietro le quinte, include è in realtà un alias per append_features , che (dai documenti):
L'implementazione predefinita di Ruby è aggiungere costanti, metodi e modulo variabili di questo modulo in aModule if questo modulo non è già stato aggiunto a un modulo o uno dei suoi antenati.
Tutte le altre risposte sono buone, incluso il suggerimento per scavare in RubySpecs:
https://github.com/rubyspec/rubyspec/ blob / master / core / modulo / include_spec.rb
https://github.com/rubyspec/rubyspec/ blob / master / core / modulo / extend_object_spec.rb
Per quanto riguarda i casi d'uso:
Se includi il modulo ReusableModule nella classe ClassThatIncludes, vengono referenziati i metodi, le costanti, le classi, i sottomoduli e altre dichiarazioni.
Se estendi la classe ClassThatExtends con il modulo ReusableModule, i metodi e le costanti vengono copiati . Ovviamente, se non stai attento, puoi sprecare molta memoria duplicando dinamicamente le definizioni.
Se si utilizza ActiveSupport :: Concern, la funzionalità .included () consente di riscrivere direttamente la classe inclusa. Il modulo ClassMethods all'interno di una preoccupazione viene esteso (copiato) nella classe inclusa.
Vorrei anche spiegare il meccanismo in cui funziona. Se non ho ragione, correggi.
Quando usiamo include
stiamo aggiungendo un collegamento dalla nostra classe a un modulo che contiene alcuni metodi.
class A
include MyMOd
end
a = A.new
a.some_method
Gli oggetti non hanno metodi, ma solo i clasi e i moduli.
Quindi quando a
riceve il messaggio some_method
inizia il metodo di ricerca some_method
nella classe eigen di a
, quindi in Una classe
e quindi collegata ai moduli della classe A
se ce ne sono (in ordine inverso, le ultime vittorie incluse).
Quando usiamo estende
stiamo aggiungendo il collegamento a un modulo nella classe eigen dell'oggetto.
Quindi se usiamo A.new.extend (MyMod) stiamo aggiungendo il collegamento al nostro modulo alla classe eigen dell'istanza di A o a '
.
E se usiamo A.extend (MyMod) stiamo aggiungendo il collegamento ad A (oggetto, le classi sono anche oggetti) eigenclass A '
.
quindi il percorso di ricerca del metodo per a
è il seguente:
a = > a '= > moduli collegati a un 'class = > A.
inoltre esiste un metodo prepend che modifica il percorso di ricerca:
a = > a '= > modulo anteposto A = > A = > modulo incluso in A
scusate il mio cattivo inglese.