2012-04-14 18 views
7

Quiero usar la función de sobrecarga en Ruby como en muchos otros idiomas, pero Ruby en sí no es compatible con esta característica.Sobrecarga en Ruby

¿Debo implementarlo de la forma que define un método con el argumento *args y determinar el número y los tipos de los argumentos dentro del método? Algunos como:

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

Pueden ver, es realmente feo que sobrecargar directamente.

Quiero saber si hay palabras clave u otras maneras (como un módulo de meta-programación) que me permitan definir un método de sobrecarga de una manera más elegante.

+1

No me gusta la opción de longitud ... ya que los argumentos se pueden pasar como nulos ... al menos esto es difícil de leer. – Arth

+3

Deberías intentar adaptarte a la manera Ruby. Hay una razón por la cual no hay sobrecarga en Ruby. Los métodos solo deberían hacer una cosa, no mágicamente decidir hacer cosas muy diferentes solo por diferentes argumentos. En su lugar, intente aprovechar [Duck Typing] (http://en.wikipedia.org/wiki/Duck_typing) y, en caso de duda, utilice diferentes métodos con nombres significativos. –

+0

Una consulta adicional: ¿Sobrecarga solo con el número de parámetros (como en su ejemplo) o también con el tipo de parámetro? O en un ejemplo: ¿El método llama 'a (1)' y 'a ('x')' a la misma llamada o hace la llamada a diferentes métodos? – knut

Respuesta

4

Usted podría intentar un poco de programación meta para llegar a su objetivo.

Véase el siguiente código:

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 

puede definir su método sobrecargado con define_overload_method. Los parámetros son el nombre del método y una lista de procedimientos. Se crea el método methodname y llama al método correspondiente. Qué método está determinado por la cantidad de parámetros (¡No escriba!).

una sintaxis alternativa sería:

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 define el marco para sus métodos sobrecargados, overload_method define una 'sobrecarga-método'.

Pero como ya se ha mentioned by Holger:

Usted debe tratar de adaptarse a la forma de Ruby. Hay una razón por la cual no hay sobrecarga en Ruby. Los métodos solo deberían hacer una cosa, no mágicamente decidir hacer cosas muy diferentes solo por diferentes argumentos. En su lugar, intente aprovechar Duck Typing y, en caso de duda, utilice diferentes métodos con nombres significativos.


tenía curiosidad cómo podría implementar una versión con el tipo de sobrecarga sensible.Aquí está:

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 

Cuando se llama con

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

Se obtiene

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

Su método es un implemento tan hermoso y podría satisfacer completamente mis requerimientos. Sin embargo, para el consejo de Holger, he decidido cambiar mi camino. Gracias, de todos modos. :) –

+0

Creo que esa es la mejor decisión;) Mientras tanto encontré una solución de tipo sobrecarga sensible. Extendí mi respuesta. – knut

+0

Pensé en construir una joya fuera de mi código. Parece que alguien más ya tenía la misma idea: http://rubygems.org/gems/overload La solución usa una lógica similar. – knut

5

Puede probar la existencia de cada argumento por separado, ya que se configuran como nulos si no se pasan (¡suponiendo que se pasen por orden!).

Si insistes en argumentos muy diferentes, sugiero un argumento hash con símbolos para cada argumento que pretendas ... y pruebas apropiadas.

** ACTUALIZACIÓN **

Además también se puede cambiar el nombre de los métodos que sobrecargan con los nombres más específicos, tales como

def perform_task_with_qualifier_1 
+1

+1 para la sugerencia de argumento hash. Para la mayoría de todo lo que usaría sobrecargas de método para, los argumentos con nombre/por defecto son más claros. – millimoose

+0

La primera forma es buena. Sin embargo, no he pensado en la forma anterior en que dijiste, pero todavía tengo curiosidad de saber si existen otras formas mejores. –

+0

Oh, lo siento, el método de cambio de nombre es justo lo que trato de evitar. –

0

La característica definitoria de sobrecarga es que la expedición sucede de forma estática. En Ruby, despacho siempre sucede dinámicamente, no hay otra manera. Por lo tanto, la sobrecarga no es posible en Ruby.