2008-10-09 31 views
63

Là một bài tập lập trình, tôi đã viết một đoạn mã Ruby tạo một lớp, khởi tạo hai đối tượng từ lớp đó, monkeypatches một đối tượng và dựa vào method_missing để monkeypatch một đối tượng khác.Ruby: define_method vs def

Đây là giao dịch. Điều này hoạt động như dự định:

class Monkey 

    def chatter 
    puts "I am a chattering monkey!" 
    end 

    def method_missing(m) 
    puts "No #{m}, so I'll make one..." 
    def screech 
     puts "This is the new screech." 
    end 
    end 
end 

m1 = Monkey.new 
m2 = Monkey.new 

m1.chatter 
m2.chatter 

def m1.screech 
    puts "Aaaaaargh!" 
end 

m1.screech 
m2.screech 
m2.screech 
m1.screech 
m2.screech 

Bạn sẽ nhận thấy rằng tôi có tham số cho method_missing. Tôi đã làm điều này bởi vì tôi đã hy vọng sử dụng define_method để tự động tạo các phương thức còn thiếu với tên thích hợp. Tuy nhiên, nó không hoạt động. Trong thực tế, thậm chí sử dụng define_method với một tên tĩnh như vậy:

def method_missing(m) 
    puts "No #{m}, so I'll make one..." 
    define_method(:screech) do 
    puts "This is the new screech." 
    end 
end 

kết thúc với kết quả như sau:

ArgumentError: wrong number of arguments (2 for 1) 

method method_missing in untitled document at line 9 
method method_missing in untitled document at line 9 
at top level in untitled document at line 26 
Program exited. 

Điều gì làm cho các thông báo lỗi bối rối hơn nữa là tôi chỉ có một đối số cho method_missing. ..

Trả lời

132

define_method là phương thức (riêng tư) của đối tượng Lớp. Bạn đang gọi nó từ một ví dụ . Không có phương pháp thể hiện nào được gọi là define_method, do đó, nó đệ quy đến số method_missing của bạn, lần này với :define_method (tên của phương thức bị thiếu) và :screech (đối số duy nhất bạn chuyển đến define_method).

Hãy thử này để thay thế (để xác định phương pháp mới trên tất cả các đối tượng khỉ):

def method_missing(m) 
    puts "No #{m}, so I'll make one..." 
    self.class.send(:define_method, :screech) do 
     puts "This is the new screech." 
    end 
end 

Hoặc này (để xác định nó chỉ trên đối tượng đó là kêu gọi, sử dụng "eigenclass" của đối tượng):

def method_missing(m) 
    puts "No #{m}, so I'll make one..." 
    class << self 
     define_method(:screech) do 
     puts "This is the new screech." 
     end 
    end 
end 
+1

Đó là một câu trả lời tuyệt vời, Avdi và nó giải thích một số câu hỏi khác mà tôi đã có. Cảm ơn bạn. – gauth

+12

Theo nguyên tắc chung, điều này cho thấy lý do tại sao bạn nên sử dụng danh sách trắng trong 'method_missing', để bạn * chỉ * xử lý các phương thức mà bạn * thực sự * quan tâm và b) chuyển tiếp mọi thứ bạn làm * không * muốn để xử lý "lên chuỗi thức ăn", sử dụng 'super'. –

+1

@ JörgWMittag có thể tôi có thêm một số từ trên dòng * b) chuyển tiếp mọi thứ bạn không muốn xử lý "lên chuỗi thức ăn", sử dụng siêu. ? * –

4

self.class.define_method (: tiếng rít) không hoạt động, vì define_method là phương pháp tin bạn có thể làm điều đó

class << self 
    public :define_method 
end 
def method_missing(m) 
puts "No #{m}, so I'll make one..." 
Monkey.define_method(:screech) do 
    puts "This is the new screech." 
end 
4
def method_missing(m) 
    self.class.class_exec do 
     define_method(:screech) {puts "This is the new screech."} 
    end 
end 

phương pháp quét sẽ có sẵn cho tất cả các đối tượng Khỉ.

Các vấn đề liên quan