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.

8 comentários:

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

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

    ResponderExcluir
  3. Anônimo5:50 PM

    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! :)

    ResponderExcluir
  4. 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 ;)

    ResponderExcluir
  5. @arthurgeek

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

    ResponderExcluir
  6. Ronie, tá sobrando tempo aí! hahhahahah

    ResponderExcluir
  7. Anônimo7:58 PM

    "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.

    ResponderExcluir
  8. Anônimo10:23 AM

    Só pra complementar o comentário acima, isso é conhecido como Polimorfismo Ad-Hoc por sobrecarga.

    ResponderExcluir