2009-01-27 32 views
29

Có thể trong Ruby để có được một tham chiếu đến phương pháp của một đối tượng (Tôi muốn biết nếu điều này có thể được thực hiện mà không procs/lambdas), ví dụ, hãy xem xét đoạn mã sau:Làm cách nào tôi có thể tham chiếu đến một phương thức?


class X 
    def initialize 
    @map = {} 
    setup_map 
    end 

    private 
    def setup_map 
    # @map["a"] = get reference to a method 
    # @map["b"] = get reference to b method 
    # @map["c"] = get referebce to c method 
    end 

    public 
    def call(a) 
    @map["a"](a) if a > 10 
    @map["b"](a) if a > 20 
    @map["c"](a) if a > 30 
    end 

    def a(arg) 
    puts "a was called with #{arg}" 
    end 

    def b(arg) 
    puts "b was called with #{arg}" 
    end 

    def c(arg) 
    puts "c was called with #{arg}" 
    end 
end 

Có có thể làm điều đó? Tôi muốn tránh procs/lambdas vì tôi muốn có thể thay đổi hành vi của A, B, C bằng cách phân lớp.

Trả lời

42

Bạn muốn Object#method:

---------------------------------------------------------- Object#method 
    obj.method(sym) => method 
------------------------------------------------------------------------ 
    Looks up the named method as a receiver in obj, returning a Method 
    object (or raising NameError). The Method object acts as a closure 
    in obj's object instance, so instance variables and the value of 
    self remain available. 

     class Demo 
      def initialize(n) 
      @iv = n 
      end 
      def hello() 
      "Hello, @iv = #{@iv}" 
      end 
     end 

     k = Demo.new(99) 
     m = k.method(:hello) 
     m.call #=> "Hello, @iv = 99" 

     l = Demo.new('Fred') 
     m = l.method("hello") 
     m.call #=> "Hello, @iv = Fred" 

Bây giờ mã của bạn trở thành:

private 
def setup_map 
    @map = { 
    'a' => method(:a), 
    'b' => method(:b), 
    'c' => method(:c) 
    } 
    # or, more succinctly 
    # @map = Hash.new { |_map,name| _map[name] = method(name.to_sym) } 
end 

public 
def call(arg) 
    @map["a"][arg] if arg > 10 
    @map["b"][arg] if arg > 20 
    @map["c"][arg] if arg > 30 
end 
+0

Tuyệt vời! Bạn là người đàn ông :) – Geo

4

Bạn có thể làm điều này với lambdas khi vẫn duy trì khả năng thay đổi hành vi trong lớp con: phương pháp

class X 
    def initialize 
    @map = {} 
    setup_map 
    end 

    private 
    def setup_map 
    @map["a"] = lambda { |a| a(a) } 
    @map["b"] = lambda { |a| b(a) } 
    @map["c"] = lambda { |a| c(a) } 
    end 

    public 
    def call(a) 
    @map["a"].call(a) if a > 10 
    @map["b"].call(a) if a > 20 
    @map["c"].call(a) if a > 30 
    end 

    def a(arg) 
    puts "a was called with #{arg}" 
    end 

    def b(arg) 
    puts "b was called with #{arg}" 
    end 

    def c(arg) 
    puts "c was called with #{arg}" 
    end 
end 
+0

Công trình này! Nhưng, không có cách nào để có được một tham chiếu thực sự đến một phương pháp? – Geo

+0

Điều tốt nhất bạn có thể làm là instance.methods và cung cấp một chuỗi các chuỗi. – Samuel

1

Ruby là đối tượng không hạng nhất; nó thực hiện OO với thông điệp đi qua.

class X 
    def call(a) 
    self.send(:a, a) if a > 10 
    self.send(:b, a) if a > 20 
    self.send(:c, a) if a > 30 
    end 

    def a(arg) 
    puts "a was called with #{arg}" 
    end 

    def b(arg) 
    puts "b was called with #{arg}" 
    end 

    def c(arg) 
    puts "c was called with #{arg}" 
    end 
end 

Hoặc chỉ cần gọi cho họ trực tiếp:

def call(a) 
    self.a(a) if a > 10 
    self.b(a) if a > 20 
    self.c(a) if a > 30 
end 
+0

Đây là một ví dụ đơn giản. Đối với các lớp tôi cần viết, tôi sẽ có khoảng 20-30 phương pháp để kiểm tra. Nếu kiểm tra sẽ không được rất phong cách, phải không? – Geo

+0

Tôi giả định các câu lệnh if là proxy cho một chuyển đổi hoặc tra cứu băm. "send (: a, a)" trông đơn giản hơn "@map [" a "] [arg]" với tôi, thậm chí nhiều hơn thế nếu có nhiều người trong số họ. – Ken

0

Bạn có thể lấy một tham chiếu đến phương pháp theo object.method(:method_name).

Ví dụ: Để tham chiếu đến phương thức system.

m = self.method(:system) 
m.call('ls) 
Các vấn đề liên quan