【问题标题】:How is the "public/protected/private" method implemented, and how can I emulate it?“public/protected/private”方法是如何实现的,我该如何模拟它?
【发布时间】:2011-12-06 12:49:25
【问题描述】:

在 ruby​​ 中,您可以这样做:

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

现在 f1 和 f3 是公共的,f2 和 f4 是私有的。内部发生了什么允许您调用类方法然后更改方法定义?如何实现相同的功能(表面上是创建自己的 java 类注解)

例如...

class Thing
  fun
  def f1
    puts "hey"
  end

  notfun
  def f2
    puts "hey"
  end
end

而 fun 和 notfun 会改变以下函数定义。

谢谢

【问题讨论】:

    标签: ruby access-specifier


    【解决方案1】:

    您有时可以将 Ruby 倒入浓缩咖啡杯中。让我们看看如何。

    这是一个模块 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
    

    ...你可以用来扩展一个类...

    class Thing
    
      extend FunNotFun
    
      fun
      def f1
        puts "hey"
      end
    
      notfun
      def f2
        puts "hey"
      end
    end
    

    ...结果如下:

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

    但请参阅下面的行以获得更好的方法。


    注解(参见 normalocity 的回答)的麻烦更少,并且作为一个常见的 Ruby 习惯用法,可以更轻松地传达代码的意图。以下是使用注释的方法:

    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
    

    在使用中,注解出现在方法定义之后,而不是之前:

    class Thing
    
      extend FunNotFun
    
      def f1
        puts "hey"
      end
      fun :f1
    
      def f2
        puts "hey"
      end
      notfun :f2
    
    end
    

    结果是一样的:

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

    【讨论】:

    • @Semyon,如果您有多个线程同时向同一个类添加方法,则不会。不过,几乎总是只通过一个线程来添加方法。
    【解决方案2】:

    听起来您想为 Ruby 语言本身编写扩展,这是可能的。这不是可以简单解释的东西,但这个链接应该让你开始:

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

    此参考与 Ruby 中的注释有关,也可能有帮助/相关:

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

    【讨论】:

    • 真的有必要扩展 ruby​​ 以使其成为可能吗?感觉应该可以通过一些聪明的元编程之类的东西来实现,但我想可能并非如此。
    【解决方案3】:

    这是一个纯红宝石解决方案,可让您朝着正确的方向前进。它取决于method_added。小心使用保护子句避免递归。

    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
    

    【讨论】:

      猜你喜欢
      • 2017-08-21
      • 2019-08-07
      • 1970-01-01
      • 1970-01-01
      • 2010-11-22
      • 1970-01-01
      • 2016-03-02
      • 2021-05-11
      • 1970-01-01
      相关资源
      最近更新 更多