Quarta-feira, Março 12, 2008

Sobrecarga de métodos em Ruby

Uma das coisas muito legais que o Java possui é sobrecarga de métodos (várias vezes confundido com polimorfismo) que é a capacidade de possuir métodos diferentes com mesmo nome, mas com parâmetros diferentes.

Bom, isso não existe em Ruby, mas nada impede que isso seja implementado. Rascunhei isso aqui:

class Object
  
  def self.generate_method_name_for(method_name, *parameter_types)
    # Usamos espaços no nome dos métodos gerados para evitar chamadas acidentais
    "#{method_name}_#{parameter_types.collect{|it| it.to_s}.join(' ')}".to_sym
  end
  
  def self.def_method(method_name, *parameter_types, &block)
  
    # Definindo o método genérico da classe,
    define_method method_name.to_sym do |*params|
      send("#{method_name}_#{params.collect{|it| it.class.to_s}.join(' ')}", *params)
    end unless self.method_defined?(method_name.to_sym)
    
    #Definindo cada método específico
    specific_method = generate_method_name_for(method_name, *parameter_types)
    
    define_method(specific_method, &block)
    
  end
end


E para usar:

class Test
  def_method :test, String, String do |a, b|
    puts "Duas strings"
  end
  
  def_method :test, String do |a|
    puts "Uma string"
  end
  
  def_method :test, Fixnum do |a|
    puts "Um número"
  end
end


x = Test.new

x.test("a", "b")
x.test("a")
x.test(1)


É lógico que isso custa desempenho, mas serve. Acho também que o código poderia ser grandemente melhorado, até pq não sei se isso funcionaria corretamente com herança e polimorfismo, mas não quis perder mais tempo brincando nisso :)

Moral da história: adoro classes abertas.

7 comentários:

Ronaldo Ferraz disse...

Caramba, esse foi o maior estupro do Ruby que eu vi até agora. :-) Existe alguma justificativa para precisar disso?

fnando disse...

Também acho! Não seria mais fácil receber um hash, por exemplo?

ArthurGeek disse...

Não seria mais simples:

class Test
def test(a,b=nil)
b ? puts "Duas strings" : puts "Uma string"
end
end

x = Test.new
x.test("a", "b")
x.test("a")

Ou, caso a lista de parâmetros seja muito grande, fico com o Nando, receber um hash! E o melhor disso: os parâmetros não precisam ser passados em uma ordem certa! :)

Ronie Uliana disse...

Huahuahuahua,

Não, não existe justificativa pra usar um código desses. Foi só pra pq um dia eu estava pensando "puxa, sobrecarga de métodos é legal, pena que Ruby não tem". Aí pensei se não seria possível fazer, já que Ruby é bem extensível. Bom... é possível :)

Aí depois de fazer isso eu olhei e pensei comigo mesmo "Nahhh... nem vale a pena" :) Mas fica como um exercício e prova de que você mesmo pode fazer muita coisa em Ruby.

Mas como toda faca afiada, se não usar com cuidado, corta o dedo ;)

Ronie Uliana disse...

@arthurgeek

Até seria possível, mas não teria como fazer sobrecarga com um Inteiro ao invés de String :)

fnando disse...

Ronie, tá sobrando tempo aí! hahhahahah

Rayson Romulo disse...

"Uma das coisas muito legais que o Java possui é sobrecarga de métodos (várias vezes confundido com polimorfismo) que é a capacidade de possuir métodos diferentes com mesmo nome, mas com parâmetros diferentes."

Na realidade e segunda a literatura, sobrecarga é um tipo de polimorfismo ("várias formas").

Pode-se definir polimorfismo como sendo a capacidade de um operador executar o comportamento adequado de acordo com o operando, onde operador/ operando pode ser: método/classe, método/parametros(sobrecarga), etc.