2011-10-12 34 views
5

Trong ruby, bạn có thể làm điều này:Phương thức "công khai/được bảo vệ/riêng tư" được triển khai như thế nào và tôi có thể mô phỏng nó như thế nào?

class Thing 
    public 
    def f1 
    puts "f1" 
    end 

    private 
    def f2 
    puts "f2" 
    end 

    public 
    def f3 
    puts "f3" 
    end 

    private 
    def f4 
    puts "f4" 
    end 
end 

nơi tại f1 và f3 và công cộng, f2 và f4 là tư nhân. Điều gì đang xảy ra bên trong cho phép bạn gọi một phương thức lớp mà sau đó thay đổi định nghĩa phương thức? Làm thế nào tôi có thể thực hiện các chức năng tương tự (bề ngoài là để tạo java của riêng tôi như chú thích)

ví dụ ...

class Thing 
    fun 
    def f1 
    puts "hey" 
    end 

    notfun 
    def f2 
    puts "hey" 
    end 
end 

và vui vẻ và notfun sẽ thay đổi định nghĩa hàm sau.

Cảm ơn

Trả lời

8

Đôi khi bạn có thể đẩy Ruby vào cốc espressso. Hãy xem làm thế nào.

Dưới đây là một mô-đun FunNotFun ...

module FunNotFun 

    def fun 
    @method_type = 'fun' 
    end 

    def notfun 
    @method_type = 'not fun' 
    end 

    def method_added(id) 
    return unless @method_type 
    return if @bypass_method_added_hook 
    orig_method = instance_method(id) 
    @bypass_method_added_hook = true 
    method_type = @method_type 
    define_method(id) do |*args| 
     orig_method.bind(self).call(*args).tap do 
     puts "That was #{method_type}" 
     end 
    end 
    @bypass_method_added_hook = false 
    end 

end 

... mà bạn có thể sử dụng để mở rộng một lớp ...

class Thing 

    extend FunNotFun 

    fun 
    def f1 
    puts "hey" 
    end 

    notfun 
    def f2 
    puts "hey" 
    end 
end 

... với kết quả này:

Thing.new.f1 
# => hey 
# => That was fun 

Thing.new.f2 
# => hey 
# => That was not fun 

Nhưng hãy xem bên dưới dòng để biết cách tốt hơn.


Chú thích (xem câu trả lời bình thường) ít gặp rắc rối hơn và là thành ngữ Ruby thông thường, sẽ dễ dàng truyền đạt ý định của mã của bạn hơn. Dưới đây là làm thế nào để làm điều đó với chú thích:

module FunNotFun 

    def fun(method_id) 
    wrap_method(method_id, "fun") 
    end 

    def notfun(method_id) 
    wrap_method(method_id, "not fun") 
    end 

    def wrap_method(method_id, type_of_method) 
    orig_method = instance_method(method_id) 
    define_method(method_id) do |*args| 
     orig_method.bind(self).call(*args).tap do 
     puts "That was #{type_of_method}" 
     end 
    end 
    end 

end 

Khi sử dụng, chú thích đưa ra sau khi phương thức được định nghĩa, chứ không phải trước đây:

class Thing 

    extend FunNotFun 

    def f1 
    puts "hey" 
    end 
    fun :f1 

    def f2 
    puts "hey" 
    end 
    notfun :f2 

end 

Kết quả là như nhau:

Thing.new.f1 
# => hey 
# => That was fun 

Thing.new.f2 
# => hey 
# => That was not fun 
+0

ah, đây là những gì tôi đã có trong tâm trí –

+0

Là cách tiếp cận đầu tiên thread-an toàn? –

+1

@Semyon, Không nếu bạn có nhiều chủ đề thêm phương thức vào cùng một lớp cùng một lúc. Gần như mọi khi, việc thêm các phương thức được thực hiện chỉ bằng một luồng. –

1

Có vẻ như bạn muốn tự viết phần mở rộng cho ngôn ngữ Ruby. Đó không phải là một cái gì đó có thể được giải thích ngắn gọn, nhưng liên kết này sẽ giúp bạn bắt đầu:

http://ruby-doc.org/docs/ProgrammingRuby/html/ext_ruby.html

Tham chiếu này, cần phải làm gì với chú thích trong Ruby, cũng có thể hữu ích/liên quan:

http://martinfowler.com/bliki/RubyAnnotations.html

+1

Có thực sự cần thiết để mở rộng ruby ​​cho điều đó để có thể? Có vẻ như nó có thể thực hiện được với một vài chương trình siêu lập trình thông minh hay một thứ gì đó, nhưng tôi đoán điều đó có thể không đúng. –

1

Đây là giải pháp tinh khiết để giúp bạn đi đúng hướng. Nó bản lề trên method_added. Hãy cẩn thận để tránh đệ quy bằng cách sử dụng một điều khoản bảo vệ.

module Annotations 
    def fun 
    @state = :fun 
    end 

    def not_fun 
    @state = :not_fun 
    end 

    def inject_label(method_name) 
    state = @state 
    define_method(:"#{method_name}_with_label") do |*args, &block| 
     puts "Invoking #{method_name} in state #{state}" 
     send(:"#{method_name}_without_label", *args, &block) 
    end 

    alias_method :"#{method_name}_without_label", :"#{method_name}" 
    alias_method :"#{method_name}", :"#{method_name}_with_label" 
    end 

    def self.extended(base) 
    base.instance_eval do 
     def self.method_added(method_name) 
     return if method_name.to_s =~ /_with(out)?_label\Z/ 
     @seen ||= {} 
     unless @seen[method_name] 
      @seen[method_name] = true 
      inject_label(method_name) 
     end 
     end 
    end 
    end 
end 

class Foo 
    extend Annotations 

    fun 

    def something 
    puts "I'm something" 
    end 

    not_fun 

    def other 
    puts "I'm the other" 
    end 
end 
Các vấn đề liên quan