2009-06-19 11 views
9

È possibile includere i moduli nelle classi per estendere la funzionalità della classe in termini di aggiunta di metodi di classe e metodi di istanza a quella particolare classe.È possibile invertire il modulo incluso in una classe?

module M 
def self.class_method_from_module 
    'from class_method_from_module' 
end 

def instance_method_from_module 
    'from instance_method_from_module' 
end 
end 

class C 
include M 

def self.class_method 
    'from class_method' 
end 

def instance_method 
    'from instance_method' 
end 
end 

puts C.class_method => 'from class_method' 

puts C.class_method_from_module => 'from class_method_from_module' 

puts C.new.instance_method => 'from instance_method' 

puts C.new.instance_method_from_module => 'instance_method_from_module' 

Ora anche dopo la rimozione del modulo M dalla memoria con il seguente:

Object.send(:remove_const, :M) #remove the module M 

puts C.class_method_from_module => 'from class_method_from_module' 

puts C.new.instance_method_from_module => 'instance_method_from_module' 

anziché metodo mancanti. Perché? Qual è il modo migliore per rimuovere la funzionalità aggiunta da un modulo a una classe?

risposta

9

I dettagli variano a seconda realizzazione, ma so che almeno in JRuby e la risonanza magnetica 1.8 v'è un costrutto chiamato un classe che viene inserito per la catena di ereditarietà di una classe quando un modulo viene esteso o incluso Includi . Pertanto, il modulo non verrà raccolto automaticamente, poiché la classe di inclusione si riferisce ancora ad esso e i metodi saranno ancora presenti nella classe. Ci sono alcuni fantastici articoli di Patrick Farley su questo argomento e argomenti correlati in his blog.

Quindi, per "rimuovere" un modulo, è possibile che indefinisca individualmente ciascun metodo proveniente dal modulo, ma questo è un meccanismo abbastanza ingombrante a tale scopo. Se l'uso di una gemma è accettabile, probabilmente sarebbe meglio usare Mixology, che è stato progettato specificamente allo scopo di aggiungere e rimuovere moduli in modo dinamico.

+2

Il link 'Mixology' ha un errore di battitura (doubled' y' in url). Ma grazie per questo! –

+0

Wow, questa è una biblioteca veramente antica ... – Hubro

5

Quando si include un mixin in una classe, si aggiungono effettivamente i metodi definiti nel modulo alla classe o si sostituiscono i metodi nella classe con quelli nel modulo che hanno lo stesso nome. La classe non ha alcun vincolo con il modulo, motivo per cui "indefinendo" il modulo M non avrà effetto sulla classe C. Tutto ciò che fa è impedire di mescolare in M oltre quel punto.

È possibile utilizzare undef_method per rimuovere i metodi dalla classe C, ma questo avrà effetti collaterali, potenzialmente - se un metodo è stato sostituito includendo un modulo, non si otterrà l'originale indietro, per esempio. L'indebolimento di un metodo di classe è un po 'brutto.

C.send(:undef_method, :instance_method_from_module) 
class << C 
    self 
end.send(:undef_method, :class_method_from_module) 
+0

Grazie per il feedback Daniel. La mia comprensione è che quando si chiama un metodo su un oggetto si cercano i metodi definiti nel metaclasse di ordine (1) di quell'oggetto particolare (2) la sua classe (3) inclusa la superclasse di modulo (4) superclasse (5) inclusa moduli e così via, quindi quando includo un modulo e aggiungo affettivamente quei metodi, come fa Ruby a sapere quale metodo appartiene a metaclasse, classe, modulo e così via ... Ho pensato che mescolando il modulo, Ruby mantiene un riferimento a quel particolare modulo, e rimuovendo la costante, dovrebbe rimuovere efficacemente il riferimento. – Dharam

+0

Per quanto ho capito, non viene mantenuto alcun riferimento al modulo; il mixin avviene, e quindi il modulo diventa (si fa per dire) parte della classe. Pertanto, anche se hai rimosso il modulo, la classe conserva ancora i metodi/proprietà misti. –

+0

Ci deve essere qualche riferimento al modulo (un qualche tipo di conoscenza che questo metodo è definito nel modulo o qualche tipo) mantenuto nella tabella di memoria, altrimenti come ruby ​​dovrebbe sapere dove cercare nella catena gerarchica quando un metodo è in realtà non definito nella classe. Grazie per il feedback però. – Dharam

Problemi correlati