242

这种创建私有类方法的方法是如何工作的:

class Person

  def self.get_name
    persons_name
  end

  class << self

    private

    def persons_name
      "Sam"
    end
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name  #=> raises "private method `persons_name' called for Person:Class (NoMethodError)"

但这不会:

class Person

  def self.get_name
    persons_name
  end

  private

  def self.persons_name
    "Sam"
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name
4

8 回答 8

296

privateself如果您在显式对象上定义方法(在您的情况下),似乎不起作用。您可以使用private_class_method将类方法定义为私有(或像您描述的那样)。

class Person
  def self.get_name
    persons_name
  end

  def self.persons_name
    "Sam"
  end

  private_class_method :persons_name
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name

或者(在 ruby​​ 2.1+ 中),由于方法定义返回方法名称的符号,您也可以按如下方式使用它:

class Person
  def self.get_name
    persons_name
  end

  private_class_method def self.persons_name
    "Sam"
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name
于 2011-02-10T03:26:38.263 回答
111

ExiRe写道:

ruby 的这种行为真是令人沮丧。我的意思是如果你移动到私人部分 self.method 那么它不是私人的。但是,如果您将其移至 class << self ,那么它会突然起作用。这太恶心了。

令人困惑的可能是,令人沮丧的可能是,但绝对不是。

一旦您了解了 Ruby 的对象模型和相应的方法查找流程,它就非常有意义,尤其是考虑到它private不是访问/可见性修饰符,而是实际上是方法调用(以类作为其接收者),如此处所讨论...... Ruby 中没有“私有部分”之类的东西。

要定义私有实例方法,您调用实例的类将随后定义的方法的默认可见性设置为私有......因此通过调用类的类来定义私有private方法非常有意义,即。它的元类。private

其他主流的、自称 OO 语言可能会给你一个不那么令人困惑的语法,但你肯定会在没有 Ruby 元编程工具的强大功能的情况下与一个令人困惑且不太一致(不一致?)的对象模型进行权衡。

于 2013-02-28T11:23:37.580 回答
87

默认情况下,所有类方法都是公共的。要使它们私有,您可以像 @tjwallace 那样使用Module#private_class_method编写或以不同方式定义它们,就像您所做的那样:

class << self

  private

  def method_name
    ...
  end
end

class << self打开 self 的单例类,这样就可以为当前的 self 对象重新定义方法。这用于定义类/模块(“静态”)方法。只有在那里,定义私有方法才能真正为您提供私有类方法。

于 2012-09-07T04:07:20.077 回答
19

为了完整起见,我们也可以避免在单独的行中声明 private_class_method。我个人不喜欢这种用法,但很高兴知道它存在。

private_class_method  def self.method_name
 ....
end
于 2015-11-05T15:35:04.033 回答
6

我也发现 Ruby(或至少我对它的了解)在这方面没有达到标准。例如以下做我想要的但很笨拙,

class Frob
    attr_reader :val1, :val2

    Tolerance = 2 * Float::EPSILON

    def initialize(val1, val2)
        @val2 = val1
        @val2 = val2
        ...
    end

    # Stuff that's likely to change and I don't want part
    # of a public API.  Furthermore, the method is operating
    # solely upon 'reference' and 'under_test' and will be flagged as having
    # low cohesion by quality metrics unless made a class method.
    def self.compare(reference, under_test)
        # special floating point comparison
        (reference - under_test).abs <= Tolerance
    end
    private_class_method :compare

    def ==(arg)
        self.class.send(:compare, val1, arg.val1) &&
        self.class.send(:compare, val2, arg.val2) &&
        ...
    end
end

我对上面代码的问题是 Ruby 语法要求和我的代码质量指标共同导致了繁琐的代码。为了让代码按我的意愿工作并让指标安静下来,我必须让 compare() 成为一个类方法。因为我不希望它成为类的公共 API 的一部分,所以我需要它是私有的,但“私有”本身是行不通的。相反,我被迫使用'private_class_method'或一些这样的解决方法。这反过来又强制对我在 '==()' 中测试的每个变量使用 'self.class.send(:compare...'。现在这有点笨拙。

于 2014-09-22T22:40:03.750 回答
6

实例方法在类定义块中定义。类方法被定义为类的单例类上的单例方法,也非正式地称为“元类”或“特征类”。private不是关键字,而是方法(Module#private)。

这是对方法self#private/的调用,A#private它为所有即将到来的实例方法定义“切换”私有访问,直到以其他方式切换:

class A
  private
    def instance_method_1; end
    def instance_method_2; end
    # .. and so forth
end

如前所述,类方法实际上是在单例类上定义的单例方法。

def A.class_method; end

或者使用特殊语法打开 A 的匿名、单例类的定义体:

class << A
  def class_method; end
end

“消息私有”的接收者——self——内部class A类对象A。class << A块内部的self是另一个对象,单例类。

下面的示例实际上调用了两个不同的方法,称为private,使用两个不同的接收者或目标进行调用。在第一部分,我们定义了一个私有实例方法(“在A类上”),在后面我们定义了一个私有类方法(实际上是A的单例类对象上的一个单例方法)。

class A
  # self is A and private call "A.private()"
  private def instance_method; end

  class << self
    # self is A's singleton class and private call "A.singleton_class.private()"
    private def class_method; end
  end
end

现在,稍微重写一下这个例子:

class A
  private
    def self.class_method; end
end

你能看出 [Ruby 语言设计者] 犯的错误吗?您为 A 的所有即将到来的实例方法打开私有访问,但继续在不同的类(单例类)上声明一个单例方法。

于 2016-11-08T17:08:05.647 回答
-8

Ruby 似乎提供了一个糟糕的解决方案。为了解释,从一个简单的 C++ 示例开始,该示例显示了对私有类方法的访问:

#include <iostream>

class C
{
    public:
        void instance_method(void)
        {
            std::cout << "instance method\n";
            class_method();  // !!! LOOK !!! no 'send' required. We can access it
                             // because 'private' allows access within the class
        }
    private:
        void static class_method(void) { std::cout << "class method\n"; }
};

int main()
{
    C c;

    c.instance_method(); // works
    // C::class_method() does not compile - it's properly private
    return 0;
}

运行上述

   % ./a.out
   instance method
   class method

现在 Ruby 似乎没有提供等价物。我认为 Ruby 的规则是,私有方法不能被接收者访问。那是,

inst.pvt_method  # FAILS
pvt_method # WORKS only within the class (good)

这对于私有实例方法来说是可以的,但会导致私有类方法出现问题。

我希望 Ruby 以这种方式运行:

class C
    def instance_method
        STDOUT << "instance method\n"

        # Simple access to the private class method would be nice:
        class_method   # DOES NOT WORK. RUBY WON'T FIND THE METHOD
        C.class_method # DOES NOT WORK. RUBY WON'T ALLOW IT

        # ONLY THIS WORKS. While I am happy such capability exists I think
        # the way 'send' should be used is when the coder knows he/she is
        # doing a no-no.  The semantic load on the coder for this is also
        # remarkably clumsy for an elegant language like ruby.
        self.class.send(:class_method)
    end

    private_class_method def self.class_method() STDOUT << "class method\n"; end
end

但是,唉,上述方法不起作用。有人知道更好的方法吗?

当我在方法之前看到“发送”时,这清楚地表明代码违反了 API 设计者的意图,但在这种情况下,设计是专门让类的实例方法调用私有类方法。

于 2020-05-18T10:37:30.857 回答
-19

从红宝石 2.3.0 开始

class Check
  def self.first_method
    second_method
  end

  private
  def self.second_method
    puts "well I executed"
  end
end

Check.first_method
#=> well I executed
于 2017-04-07T11:09:25.283 回答