我正在寻找一种使方法“个人化”的方法 - 注意不是私人的课程
这是一个例子 - “个人”我指的是方法“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)
答案 0 :(得分:3)
不要创建“个人”方法,而是更改继承结构。
您似乎希望C类只具有B类的一些相同功能,而不会更改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
答案 1 :(得分:2)
没有标准的方法可以做到这一点。它规避了继承的工作方式。您可以实现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}中private :foo
。
(这根本没有优化,我没有实现public
和private
所具有的零参数行为,因为坦率地说这是一个巨大的PITA做得对,即便如此它也是一个黑客。)
答案 2 :(得分:2)
似乎可能令人困惑,但这里有一个选择:
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
更一般地说,使用类似的方法,您始终可以向给定实例添加方法,这当然不会影响继承的类,也不会影响同一类的其他实例。
如果你告诉我们你最终想要完成的事情,我们可能会更有帮助。
答案 3 :(得分:0)
回答这个问题有点棘手,因为我没有真正看到你想要在实践中完成什么,但是你可以尝试类似
class C < B
def foo
self.class.ancestors[-3].instance_method(:foo).bind(self).call
end
end
(ancestors[-3]
假设A继承自Object和Kernel,并且您的意图是从最顶层的非内置类访问该方法。当然,您可以用self.class.ancestors[-3]
替换A
或者自己从数组ancestors
中找出类等。)
在实践中,如果您可以在新B
之前修改alias :foo_from_A :foo
中的class B < A
,那么在课程def foo
中对原文进行别名会更简单,那么您可以在foo_from_A
中拨打C
。或者只是在C
中重新定义您想要的内容。或者以不同的方式设计整个类层次结构。
答案 4 :(得分:0)
您可以编写快捷方式来处理个性化方法。
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) # => #<UnboundMethod: C(A)#foo>
c.bam #throws NoMethodError
答案 5 :(得分:0)
有时你并不真正想要一个“是一个”(继承)关系。有时你想要的是“嘎嘎叫”。通过使用模块“混合”方法,可以轻松地在“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"