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上定义一个类似于public
和private
。
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
。
(这完全没有优化,我没有实现public
和private
的零参数行为,因为坦率地说,这是一个巨大的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"