ruby中的“个人”方法

我正在寻找一种使方法“个人化”的方法 – 注意不要私有给一个class级

这是一个例子 – 通过“个人”我的意思是方法“foo”的行为

class A def foo "foo" end end class B < A def foo "bar" end end class C < B end a=A.new; b=B.new;c=C.new 

我正在寻找一种产生以下行为的方法

 a.foo #=> "foo" b.foo #=> "bar" c.foo #=> "foo" (ultimate base class method called) 

不要创建“个人”方法,而是更改inheritance结构。

看来您希望C类只具有B类的一些相同function,而不会更改A类。

 class A def foo "foo" end end class BnC < A end class B < BnC def foo "bar" end end class C < BnC end a=A.new; b=B.new;c=C.new 

没有标准的方法可以做到这一点。 它规避了inheritance的工作方式。 您可以实现B的方法来执行这样的逻辑:

 def foo instance_of?(B) ? "bar" : super end 

当然,你可以在Class上定义一个类似于publicprivate

 class Class def personal(*syms) special_class = self syms.each do |sym| orig = instance_method(sym) define_method(sym) {|*args| instance_of?(special_class) ? orig.bind(self).call(*args) : super} end end end 

然后你可以personal :foo在B中personal :foo就像你private :foo

(这完全没有优化,我没有实现publicprivate的零参数行为,因为坦率地说,这是一个巨大的PITA做得正确,即使那时它也是一个黑客。)

似乎它可能令人困惑,但这里有一个选择:

 class A def foo "foo" end end class B < A def initialize #when constructing, add the new foo method to each instance def self.foo "bar" end end end class C < B def initialize #when constructing, do nothing end end 

更一般地,使用类似的方法,您始终可以向给定实例添加方法,这当然对inheritance的类或实际上对同一类的其他实例没有影响。

如果您向我们详细说明您最终要完成的任务,我们可能会更有帮助。

回答这个有点棘手,因为我没有真正看到你想要在实践中完成什么,但你可以尝试类似的东西

 class C < B def foo self.class.ancestors[-3].instance_method(:foo).bind(self).call end end 

ancestors[-3]假设Ainheritance自Object和Kernel,你的意图是从最顶层的非内置类访问该方法。当然你可以用self.class.ancestors[-3]替换self.class.ancestors[-3] ,或者从Array ancestors那里弄清楚这个类等等)

在实践中,如果你可以修改它,那么在B类中对原语进行别名会更简单(即alias :foo_from_A :foo在新的def foo之前的class B < A alias :foo_from_A :foo def foo ,然后你可以在C调用foo_from_A )。 或者只是重新定义你想要的C 。 或者以不同方式设计整个类层次结构

您可以编写快捷方式来处理个性化方法。

 def personalize(*methodNames) old_init = instance_method(:initialize) klass = self modul = Module.new { methodNames.each { |m| define_method(m, klass.instance_method(m)) if klass.method_defined?(m) } } methodNames.each { |m| remove_method(m) if method_defined?(m) } define_method(:initialize) { |*args| # I don't like having to check instance_of?, but this is the only way I # could thing of preventing the extension of child classes. At least it only # has to happen once, during initialization. extend modul if instance_of?(klass) old_init.bind(self).call(*args) } self end class A def foo "foo" end end class B < A def foo "bar" end def bam 'bug-AWWK!' end personalize :foo, :bam, :nometh end class C < B end a=A.new; b=B.new; c=C.new a.foo #=> "foo" b.foo #=> "bar" b.bam #=> "bug-AWWK!" c.foo #=> "foo" C.instance_method(:foo) # => # c.bam #throws NoMethodError 

有时你并不真正想要一个“是一个”(inheritance)关系。 有时你想要的是“嘎嘎叫”。 通过使用模块“混合”方法,可以轻松地在“quacks like a”类之间共享代码:

 #!/usr/bin/ruby1.8 module BasicFoo def foo "foo" end end class A include BasicFoo end class B def foo "bar" end end class C include BasicFoo end p A.new.foo # => "foo" p B.new.foo # => "bar" p C.new.foo # => "foo"