将实例方法委托给类方法
在 Ruby 中,假设我有一个类 Foo
来允许我对我的大量 Foo 集合进行编目。所有 Foo 都是绿色且球形的,这是自然的基本法则,因此我将类方法定义如下:
class Foo
def self.colour
"green"
end
def self.is_spherical?
true
end
end
这允许我这样做
Foo.colour # "green"
,但
my_foo = Foo.new
my_foo.colour # Error!
尽管事实上 my_foo
显然是绿色的。
显然,我可以定义一个实例方法colour
,它调用self.class.colour
,但如果我有许多这样的基本特征,那就变得很笨拙。
我大概还可以通过定义 method_missing
来尝试该类中是否有任何缺失的方法来做到这一点,但我不清楚这是我应该做的事情还是一个丑陋的黑客,或者如何安全地做到这一点(特别是当我实际上在 Rails 中的 ActiveRecord 下时,我知道它通过 method_missing 做了一些聪明有趣的事情)。
你会推荐什么?
In Ruby, suppose I have a class Foo
to allow me to catalogue my large collection of Foos. It's a fundamental law of nature that all Foos are green and spherical, so I have defined class methods as follows:
class Foo
def self.colour
"green"
end
def self.is_spherical?
true
end
end
This lets me do
Foo.colour # "green"
but not
my_foo = Foo.new
my_foo.colour # Error!
despite the fact that my_foo
is plainly green.
Obviously, I could define an instance method colour
which calls self.class.colour
, but that gets unwieldy if I have many such fundamental characteristics.
I can also presumably do it by defining method_missing
to try the class for any missing methods, but I'm unclear whether this is something I should be doing or an ugly hack, or how to do it safely (especially as I'm actually under ActiveRecord in Rails, which I understand does some Clever Fun Stuff with method_missing).
What would you recommend?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
Ruby 附带的 Forwardable 模块可以很好地做到这一点:
The Forwardable module that comes with Ruby will do this nicely:
如果它是普通的 Ruby,那么使用 Forwardable 是正确的答案
如果是 Rails,我会使用 delegate,例如
If it's plain Ruby then using Forwardable is the right answer
In case it's Rails I would have used delegate, e.g.
您可以使用模块:
有点难看,但比使用
method_missing
更好。我会尝试在其他答案中添加其他选项......You could use a module:
A little ugly, but better than using
method_missing
. I'll try to put other options in other answers...从设计的角度来看,我认为,即使所有 Foos 的答案都是相同的,颜色和球形?是 Foo 实例的属性,因此应该定义为实例方法而不是类方法。
然而,我可以看到在某些情况下您会想要这种行为,例如当您的系统中有
Bars
时,所有这些都是蓝色的,并且您在代码中的某个位置传递了一个类,并且想知道什么在类上调用new
之前,实例的颜色将会改变。另外,您是正确的,ActiveRecord 确实广泛使用
method_missing
例如用于动态查找器,因此如果您沿着这条路线走下去,您需要确保您的 method_missing 调用超类中的方法(如果它确定方法名称不是它可以自行处理的名称。From a design perspective, I would argue that, even though the answer is the same for all
Foos
, colour and spherical? are properties of instances ofFoo
and as such should be defined as instance methods rather than class methods.I can however see some cases where you would want this behaviour e.g. when you have
Bars
in your system as well all of which are blue and you are passed a class somewhere in your code and would like to know what colour an instance will be before you callnew
on the class.Also, you are correct that ActiveRecord does make extensive use of
method_missing
e.g. for dynamic finders so if you went down that route you would need to ensure that your method_missing called the one from the superclass if it determined that the method name was not one that it could handle itself.我认为最好的方法是使用 Dwemthy 的数组方法。
我要查找并填写详细信息,但这是骨架
编辑:耶!在职的!
I think that the best way to do this would be to use the Dwemthy's array method.
I'm going to look it up and fill in details, but here's the skeleton
EDIT: Yay! Working!
您还可以这样做:
You can also do this:
您可以定义一个直通设施:
我通常不喜欢使用
eval
,但在这里它应该非常安全。You could define a passthrough facility:
I don't generally like using
eval
, but it should be pretty safe, here.这听起来有点逃避,但实际上很少需要这样做,因为您可以同样轻松地调用 Foo.color。例外情况是,如果您有许多定义了颜色方法的类。 @var 可能是多个类之一,并且您无论如何都想显示颜色。
在这种情况下,我会问自己在哪里更多地使用该方法 - 在类上还是在模型上?它几乎总是其中之一,并且将其设为实例方法没有任何问题,即使它预计在所有实例中都是相同的。
在极少数情况下,您希望两者都可以“调用”该方法,您可以执行 @var.class.color (无需创建特殊方法)或创建一个特殊方法,如下所示:
def color
自我类别.颜色
我肯定
会避免包罗万象的(method_missing)解决方案,因为它使您不必真正考虑每个方法的用法,以及它是否属于类或实例级别。
This is going to sound like a bit of a cop out, but in practice there's rarely a need to do this, when you can call Foo.color just as easily. The exception is if you have many classes with color methods defined. @var might be one of several classes, and you want to display the color regardless.
When that's the case, I'd ask yourself where you're using the method more - on the class, or on the model? It's almost always one or the other, and there's nothing wrong with making it an instance method even though it's expected to be the same across all instances.
In the rare event you want the method "callable" by both, you can either do @var.class.color (without creating a special method) or create a special method like so:
def color
self.class.color
end
I'd definitely avoid the catch-all (method_missing) solution, because it excuses you from really considering the usage of each method, and whether it belongs at the class or instance level.