Ruby 通过类层次结构链接而不调用“super”或发明新名称
我有使用Ruby 通过类层次结构链接而不调用“super”或发明新名称,ruby,inheritance,Ruby,Inheritance,我有使用super的方法: class A def say txt puts "A.say from #{self}: #{txt}" end def run puts "A.run from #{self}: I am preparing something here..." end end class B < A def run super say 'I am B, adding 1' say 'I am B, adding
super
的方法:
class A
def say txt
puts "A.say from #{self}: #{txt}"
end
def run
puts "A.run from #{self}: I am preparing something here..."
end
end
class B < A
def run
super
say 'I am B, adding 1'
say 'I am B, adding 2'
end
end
class C < A
def run
super
say 'I am C, adding 3'
say 'I am C, adding 4'
end
end
class D < C
def run
super
say 'I am D, adding 5'
end
end
实际上只有叶类从中受益。因此,我考虑将run
方法的代码保持为块,并尝试按类层次结构的顺序调用它们。代码如下:
class A
def say txt
puts "A.say from #{self}: #{txt}"
end
def self.run &block
@run_block = block
end
def self.all_run_blocks
if self == A
return [@run_block]
else
return superclass.all_run_blocks + [@run_block]
end
end
run do
puts "A.run from #{self}: I am preparing something here..."
end
def run_all
self.class.all_run_blocks.each do |block|
block.call unless block.nil?
end
end
end
class B < A
run do
say 'I am B, adding 1'
say 'I am B, adding 2'
end
end
class C < A
run do
say 'I am C, adding 3'
say 'I am C, adding 4'
end
end
class D < C
run do
say 'I am D, adding 5'
end
end
b = B.new
d = D.new
b.run_all
d.run_all
A类
def say txt
放置“A.say from#{self}:#{txt}”
结束
def自运行和阻塞
@运行块=块
结束
def self.all_run_块
如果self==A
返回[@run_block]
其他的
返回超类。所有运行块+[@run\u block]
结束
结束
跑步
放上“A.从#{self}开始跑步:我正在准备一些东西……”
结束
def run_all
self.class.all_run_block.each do|block|
block.call,除非block.nil?
结束
结束
结束
B类
但这不起作用,因为传递给run
类方法的块仍在类的范围内,因此不知道say
实例方法
有没有更好的方法来实现这样的目标?模块#prepend
是否正是为了实现这一目标:
module A
def say txt
puts "A.say from #{self}: #{txt}"
end
def run
puts "A.run from #{self}: I am preparing something here..."
super
end
end
class B
prepend A
def run
say 'I am B, adding 1'
say 'I am B, adding 2'
end
end
class C
prepend A
def run
say 'I am C, adding 3'
say 'I am C, adding 4'
super
end
end
class D
prepend C
def run
say 'I am D, adding 5'
end
end
根据(谢谢!)中的建议,我得出以下结论:
class A
def say txt
puts "A.say from #{self}: #{txt}"
end
def run
puts "A.run from #{self}: I am preparing something here..."
end
def run_all
self.class.ancestors.reverse.each do |ancestor|
# check if ancestor is a subclass of A
if ancestor.ancestors.include? A
# check if the run method is actually from this ancestor
if ancestor.instance_method(:run).owner == ancestor
# call the run method in the context of the given instance
ancestor.instance_method(:run).bind(self).call
end
end
end
end
end
class B < A
def run
say 'I am B, adding 1'
say 'I am B, adding 2'
end
end
class C < A
def run
say 'I am C, adding 3'
say 'I am C, adding 4'
end
end
class D < C
def run
say 'I am D, adding 5'
end
end
b = B.new
d = D.new
b.run_all
d.run_all
A类
def say txt
放置“A.say from#{self}:#{txt}”
结束
def运行
放上“A.从#{self}开始跑步:我正在准备一些东西……”
结束
def run_all
self.class.祖先.reverse.each do|祖先|
#检查祖先是否是
如果祖先包括在内?A.
#检查run方法是否确实来自此祖先
如果祖先.instance_方法(:run).owner==祖先
#在给定实例的上下文中调用run方法
祖先.instance\u方法(:run.bind(self.call)
结束
结束
结束
结束
结束
B类
这不使用递归调用,而是通过类的祖先
进行迭代,找到它们的运行
方法,并按顺序调用它们
如果你知道如何改进,请告诉我 首先,我想说这是一个非常有趣的问题,如果可以,我会再次投票。谢谢你提出了一个需要思考和努力的问题,我希望我的回答能满足你的要求 注意:我使用了您的显式示例,因此此方法不接受参数。处理参数也需要一些额外的工作 为什么不把它变成一个
模块
,这样你就可以在任何地方使用它了
module Appendable
def self.extended(base)
base.include(InstanceMethods)
end
def append_method(method_name,&block)
alias_name = create_alias(method_name)
define_method(method_name) do
run_callbacks(self,alias_name) do |object|
object.instance_eval &block
end
end
end
module InstanceMethods
def run_callbacks(object,method_name)
object.send(method_name)
yield(self)
end
end
private
def create_alias(method_name)
alias_name = "_#{method_name}_callback_#{self.name}"
alias_method alias_name, method_name
alias_name
end
end
它只是将原始方法(run
在本例中)方法别名为\u METHODNAME\u callback\u CLASSNAME
,然后重新定义原始方法,首先调用别名版本,该别名版本创建链上的递归,然后将返回到类中给定的块
然后您只需像这样定义类:
class A
extend Appendable
def run
say "A"
end
def say(txt)
# I used puts to make it evident that it ran up the chain
puts "#{self.class.name} said #{txt}"
end
end
class B < A
append_method :run do
say "B"
end
end
class C < B
append_method :run do
say "C"
end
end
class D < A
append_method :run do
say "D"
end
end
class E < A
def run
puts "E overwrote run"
end
end
class F < E;append_method(:run) { say "F" }; end
除非我误解了,否则这似乎就是你想要的整体效果?run\u sub
在模式行话中被称为模板方法;这可能会帮你找到一些关于这个的资源。我认为你可能在某种元编程方面走对了方向。我可能会编写一个递归函数来遍历类层次结构,并使用相同的名称执行每个超类方法,直到超类不再定义该方法为止[self.class.superclass.instance_method(method).bind(self.call)]感谢您的关注。这段代码有两个问题:1)当“运行”d
(d
类的实例)时,它不会调用C
的run
(就像在我的原始代码中一样)和2)在原始代码中,prepend A
行只是替换了super
行,所以它不会更短。我修复了第一个问题。关于第二点,<代码>准备一个替换“<代码> <代码> >的必要性,因此,如果你坚持短代码,你可以考虑使用一个分号,如“代码”> B类,将其放在一行中;在A前面加上前缀
。我认为没有比这更好的方法了。缩短代码是本练习的全部目的:)…重点是A
的子类应该能够扩展run
函数,而不是替换它。这就是我想说的,在我的作品中提到了DSL(你在编辑它时删除了它)。类B
、C
和D
是由“最终用户”编写的,应该尽可能简短、干净、清晰,这就是为什么我提出了块的想法(不幸的是,它没有起作用)。我认为@mahemoff的评论是正确的,但我还没有从中得到任何代码。看看我刚刚发布的模式,告诉我这是否是你想要的,或者你是否想要改变什么。
class A
extend Appendable
def run
say "A"
end
def say(txt)
# I used puts to make it evident that it ran up the chain
puts "#{self.class.name} said #{txt}"
end
end
class B < A
append_method :run do
say "B"
end
end
class C < B
append_method :run do
say "C"
end
end
class D < A
append_method :run do
say "D"
end
end
class E < A
def run
puts "E overwrote run"
end
end
class F < E;append_method(:run) { say "F" }; end
A.new.run
A said A
B.new.run
B said A
B said B
C.new.run
C said A
C said B
C said C
D.new.run
D said A
D said D
F.new.run
E overwrote run
F said F