这种创建私有类方法是如何工作的:
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
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的设计者的意图,但在这种情况下,设计是特别的
要使类的实例方法调用私有类方法。
如果你在显式对象(在你的例子中是self)上定义一个方法,Private似乎不起作用。您可以使用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
ExiRe写道:
ruby的这种行为真是令人沮丧。我的意思是如果你移动
以私室自居。方法,那么它不是私有的。但是,如果
你把它移到类<< self,然后它突然工作了。
这太恶心了。
它可能令人困惑,可能令人沮丧,但绝对不会令人恶心。
一旦你理解了Ruby的对象模型和相应的方法查找流,这就非常有意义了,尤其是考虑到private不是一个访问/可见性修饰符,而是一个方法调用(类作为它的接收者),就像这里讨论的那样……在Ruby中没有所谓的“私有部分”。
要定义私有实例方法,您可以在实例的类上调用private来将随后定义的方法的默认可见性设置为private…因此,通过在类的类上调用private来定义私有类方法是非常有意义的。它的元类。
其他主流的、自称为OO的语言可能会提供不那么令人困惑的语法,但在没有Ruby元编程功能的情况下,你肯定会牺牲掉一个令人困惑且不太一致(不一致?)的对象模型。
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的设计者的意图,但在这种情况下,设计是特别的
要使类的实例方法调用私有类方法。
我也发现Ruby(或者至少我对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
My problems with the code above is that the Ruby syntax requirements
and my code quality metrics conspire to made for cumbersome code.
To have the code both work as I want and to quiet the metrics, I must
make compare() a class method. Since I don't want it to be part of
the class' public API, I need it to be private, yet 'private' by
itself does not work. Instead I am force to use 'private_class_method'
or some such work-around. This, in turn, forces the use of
'self.class.send(:compare...' for each variable I test in '==()'.
Now that's a bit unwieldy.