2012-04-14 11 views
7

Desidero utilizzare la funzionalità di sovraccarico in Ruby come molti altri linguaggi, ma Ruby non supporta questa funzionalità.Sovraccarico in Ruby

Devo implementarlo utilizzando il metodo che definisce un metodo con l'argomento *args e determinare il numero e i tipi degli argomenti all'interno del metodo? Alcuni simili:

class A 
    def foo(*args) 
     case (args.length) 
     when 1 
      do something 
     when 2 
      do something-else 
     .... 
     end 
    end 
end 

Si può vedere, è davvero brutto che direttamente sovraccarico.

Voglio sapere se ci sono parole chiave o altri modi (come un modulo di meta-programmazione) che potrebbero permettermi di definire un metodo di sovraccarico in un modo più elegante.

+1

Non mi piace l'opzione di lunghezza ... come args può essere passato come zero .. per lo meno questo è difficile da leggere. – Arth

+3

Dovresti provare ad adattarti al modo Ruby. C'è una ragione per cui non c'è sovraccarico in Ruby. I metodi dovrebbero fare solo una cosa, non decidere magicamente di fare cose molto diverse solo a causa di argomenti diversi. Invece cerca di approfittare di [Duck Typing] (http://en.wikipedia.org/wiki/Duck_typing) e in caso di dubbio, usa metodi diversi con nomi significativi. –

+0

Un'ulteriore richiesta: sovraccarico solo con il numero di parametri (come nel tuo esempio) o anche con il tipo di parametro? O in un esempio: il metodo chiama 'a (1)' e 'a ('x')' la stessa chiamata o esegue la chiamata con metodi diversi? – knut

risposta

4

Si potrebbe provare un po 'di programmazione meta per raggiungere il tuo obiettivo.

vedere il codice seguente:

class OverloadError < ArgumentError; end 
class Class 
=begin rdoc 

=end 
    def define_overload_method(methodname, *methods)  
    methods.each{ | proc | 
     define_method("#{methodname}_#{proc.arity}".to_sym, &proc) 
    } 
    define_method(methodname){|*x| 
     if respond_to?("#{methodname}_#{x.size}") 
     send "#{methodname}_#{x.size}", *x 
     else 
     raise OverloadError, "#{methodname} not defined for #{x.size} parameters" 
     end 
    } 

    end 
end 

class X 
    define_overload_method :ometh, 
     Proc.new{ "Called me with no parameter" }, 
     Proc.new{ |p1| "Called me with one parameter (#{p1.inspect})" }, 
     Proc.new{ |p1,p2| "Called me with two parameter (#{p1.inspect}, #{p2.inspect})" } 
end 

x = X.new 

p '----------' 
p x.ometh() 
p x.ometh(1) 
p x.ometh(1,2) 
p x.ometh(1,2,3) #OverloadError 

È possibile definire il metodo di overload con define_overload_method. I parametri sono il nome del metodo e un elenco di procedure. Il metodo methodname viene creato e chiama il metodo corrispondente. Quale metodo è determinato dal numero di parametri (Non digitare!).

Una sintassi alternativa sarebbe:

class OverloadError < ArgumentError; end 
class Class 
    def def_overload(methodname)  
    define_method(methodname){|*x| 
     if respond_to?("#{methodname}_#{x.size}") 
     send "#{methodname}_#{x.size}", *x 
     else 
     raise OverloadError, "#{methodname} not defined for #{x.size} parameters" 
     end 
    } 
    end 
    def overload_method(methodname, proc)  
    define_method("#{methodname}_#{proc.arity}".to_sym, &proc) 
    end 
end 
class X 
    def_overload :ometh 
    overload_method :ometh, Proc.new{ "Called me with no parameter" } 
    overload_method :ometh, Proc.new{ |p1| "Called me with one parameter (#{p1.inspect})" } 
    overload_method :ometh, Proc.new{ |p1,p2| "Called me with two parameter (#{p1.inspect}, #{p2.inspect})" } 
end 

def_overload definisce la cornice per i metodi di overload, overload_method definisce un 'sovraccarico metodo'.

Ma, come già mentioned by Holger:

Si dovrebbe cercare di adattarsi al modo in cui Ruby. C'è una ragione per cui non c'è sovraccarico in Ruby. I metodi dovrebbero fare solo una cosa, non decidere magicamente di fare cose molto diverse solo a causa di argomenti diversi. Cerca invece di sfruttare Duck Typing e, se sei in dubbio, usa metodi diversi con nomi significativi.


Ero curioso come ho potuto implementare una versione con il tipo di sovraccarico sensibile.Eccolo:

class OverloadError < ArgumentError; end 
class Class 
    def def_overload(methodname)  
    define_method(methodname){|*x| 
     methname = "xxx" 
     methname = "#{methodname}_#{x.size}#{x.map{|p| p.class.to_s}.join('_')}" 
     if respond_to?(methname) 
     send methname, *x 
     elsif respond_to?("#{methodname}_#{x.size}") 
     send "#{methodname}_#{x.size}", *x 
     else 
     raise OverloadError, "#{methodname} not defined for #{x.size} parameters" 
     end 
    } 
    end 
    def overload_method(methodname, *args, &proc)  
    types = [] 
    args.each{|arg| types << arg.to_s} 
    define_method("#{methodname}_#{proc.arity}#{types.join('_')}".to_sym, &proc) 
    end 
end 
class X 
    def_overload :ometh 
    overload_method(:ometh){ "Called me with no parameter" } 
    overload_method(:ometh, String){ |p1| "Called me with one string parameter (#{p1.inspect})" } 
    overload_method(:ometh){ |p1| "Called me with one parameter (#{p1.inspect})" } 
    overload_method(:ometh){ |p1,p2| "Called me with two parameter (#{p1.inspect}, #{p2.inspect})" } 
end 

Quando si chiama con

p x.ometh(1) 
p x.ometh('a') 

Si ottiene

"Called me with one parameter (1)" 
    "Called me with one string parameter (\"a\")" 
+0

Il tuo metodo è uno strumento così bello e potrebbe soddisfare completamente il mio requisito.Tuttavia, per il consiglio di Holger, ho deciso di cambiare il mio modo. Grazie, comunque. :) –

+0

Penso che sia la decisione migliore;) Nel frattempo ho trovato una soluzione di sovraccarico sensitve di tipo. Ho esteso la mia risposta. – knut

+0

Ho pensato di costruire una gemma dal mio codice. Sembra che qualcun altro avesse già la stessa idea: http://rubygems.org/gems/overload La soluzione utilizza una logica simile. – knut

5

È possibile verificare l'esistenza di ciascun argomento separatamente poiché sono impostati su zero se non vengono passati (supponendo che siano passati in ordine!).

Se si insistono su argomenti molto diversi, suggerisco un argomento di hash con simboli per ogni argomento che si intende .. e test di approriazione.

** UPDATE **

Inoltre, si potrebbe anche rinominare i metodi che sovraccaricano con i nomi più specifici, come ad esempio

def perform_task_with_qualifier_1 
+1

+1 per il suggerimento dell'argomento hash. Per la maggior parte di tutto si utilizzano overload di metodi, gli argomenti named/default sono più chiari. – millimoose

+0

Il primo modo è buono. Eppure non ho pensato al modo in cui hai detto, ma sono ancora curioso di sapere se esistono altri modi migliori. –

+0

Oh, mi dispiace, il metodo di rinomina è proprio quello che sto cercando di evitare. –

0

La caratteristica distintiva di sovraccarico è che la spedizione accade in modo statico. In Ruby, la spedizione sempre avviene in modo dinamico, non c'è altro modo. Pertanto, l'overloading non è possibile in Ruby.