2012-04-14 30 views
7

Tôi muốn sử dụng tính năng quá tải trong Ruby giống như nhiều ngôn ngữ khác, nhưng bản thân Ruby không hỗ trợ tính năng này.Quá tải trong Ruby

Tôi có phải thực hiện nó bằng cách xác định phương pháp với đối số *args và xác định số lượng và loại đối số bên trong phương pháp không? Một số loại như:

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

Bạn có thể thấy, nó thực sự xấu hơn quá tải trực tiếp.

Tôi muốn biết liệu có bất kỳ từ khóa nào hay cách cư xử nào khác (như mô-đun lập trình meta) có thể cho phép tôi định nghĩa phương thức nạp chồng theo cách thanh lịch hơn.

+1

Tôi không thích tùy chọn độ dài ... vì arg có thể được chuyển thành nil .. ít nhất thì điều này rất khó đọc. – Arth

+3

Bạn nên cố gắng thích ứng với cách Ruby. Có một lý do tại sao không có quá tải trong Ruby. Phương pháp chỉ nên làm một điều, không phải là một cách kỳ diệu quyết định làm những điều khác biệt rất lớn chỉ vì những lập luận khác nhau. Thay vào đó, hãy tận dụng lợi thế của [Nhập tên con vịt] (http://en.wikipedia.org/wiki/Duck_typing) và nếu nghi ngờ, hãy sử dụng các phương pháp khác nhau với các tên có ý nghĩa. –

+0

Một yêu cầu khác: Chỉ tải quá nhiều với số tham số (như trong ví dụ của bạn) hoặc cũng với loại tham số? Hoặc trong một ví dụ: Phương thức có gọi 'a (1)' và 'a ('x')' cùng một cuộc gọi hoặc thực hiện cuộc gọi các phương thức khác nhau không? – knut

Trả lời

4

Bạn có thể thử một số chương trình meta để đạt được mục tiêu của bạn.

Xem đoạn mã sau:

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 

Bạn có thể xác định phương pháp quá tải của bạn với define_overload_method. Tham số là tên phương thức và danh sách các thủ tục. Phương thức methodname được tạo và gọi phương thức tương ứng. Phương pháp nào được xác định bởi số tham số (Không gõ!).

Một cú pháp thay thế sẽ là:

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 xác định khung cho các phương pháp quá tải của bạn, overload_method định nghĩa một 'quá tải-phương pháp'.

Nhưng như đã mentioned by Holger:

Bạn nên cố gắng để thích ứng với cách Ruby. Có một lý do tại sao không có quá tải trong Ruby. Phương pháp chỉ nên làm một điều, không phải là một cách kỳ diệu quyết định làm những điều khác biệt rất lớn chỉ vì những lập luận khác nhau. Thay vào đó, hãy thử tận dụng lợi thế của Duck Typing và nếu nghi ngờ, hãy sử dụng các phương thức khác nhau với các tên có ý nghĩa.


Tôi đã tò mò làm thế nào tôi có thể thực hiện một phiên bản với loại quá tải nhạy cảm.Ở đây là:

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 

Khi bạn gọi nó với

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

Bạn nhận được

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

Phương pháp của bạn là một công cụ tuyệt vời và nó có thể đáp ứng yêu cầu của tôi một cách hoàn toàn. Tuy nhiên, theo lời khuyên của Holger, tôi đã quyết định thay đổi cách của mình. :) –

+0

Tôi nghĩ đó là quyết định tốt hơn;) Trong khi chờ đợi, tôi tìm thấy một giải pháp quá tải kiểu nhạy cảm. Tôi mở rộng câu trả lời của mình. – knut

+0

Tôi nghĩ rằng để xây dựng một viên ngọc từ mã của tôi. Có vẻ như ai đó đã có cùng ý tưởng: http://rubygems.org/gems/overload Giải pháp sử dụng một logic tương tự. – knut

5

Bạn có thể kiểm tra sự tồn tại của mỗi đối số riêng biệt khi chúng được đặt thành không nếu không được chuyển (giả sử chúng được truyền theo thứ tự!).

Nếu bạn nhấn mạnh vào các đối số rất khác nhau, tôi đề xuất một đối số băm với các biểu tượng cho mỗi đối số bạn dự định .. và thử nghiệm approriate.

** CẬP NHẬT **

Ngoài ra bạn cũng có thể đổi tên các phương pháp quá tải với những cái tên cụ thể hơn, chẳng hạn như

def perform_task_with_qualifier_1 
+1

+1 cho đề xuất đối số băm. Đối với hầu hết mọi thứ bạn muốn sử dụng quá tải phương thức cho, đối số được đặt tên/mặc định rõ ràng hơn. – millimoose

+0

Cách đầu tiên là tốt. Tuy nhiên, tôi đã không suy nghĩ về cách trước đây bạn nói, nhưng tôi vẫn còn tò mò cho dù có những cách khác tốt hơn tồn tại. –

+0

Ồ, xin lỗi, đổi tên phương pháp chỉ là những gì tôi đang cố gắng tránh. –

0

Các đặc tính của quá tải được công văn điều đó xảy ra tĩnh. Trong Ruby, công văn luôn luôn diễn ra tự động, không có cách nào khác. Do đó, quá tải là không thể trong Ruby.