从超类调用子类方法

Sha*_*aun 17 ruby oop ruby-on-rails

前言:这是在Rails应用程序的上下文中.然而,问题是Ruby特有的.

假设我有一个Media对象.

class Media < ActiveRecord::Base
end
Run Code Online (Sandbox Code Playgroud)

我在几个子类中扩展了它:

class Image < Media
  def show
    # logic 
  end
end

class Video < Media
  def show
    # logic 
  end  
end
Run Code Online (Sandbox Code Playgroud)

Media类中,我想show从适当的子类调用实现.所以,从Media,如果self是a Video,那么它将调用Video的show方法.如果self是一个Image,它将调用Image的show方法.

来自Java背景,突然出现的第一件事是"在超类中创建一个抽象方法".但是,我在几个地方(包括Stack Overflow)读过抽象方法不是在Ruby中处理这个问题的最佳方法.

考虑到这一点,我开始研究类型转换,并发现这也是Java思维的遗留物,在处理Ruby时我需要消除我的想法.

打败了,我开始编写看起来像这样的东西:

def superclass_method
  # logic
  this_media = self.type.constantize.find(self.id)
  this_media.show      
end
Run Code Online (Sandbox Code Playgroud)

我已经在Ruby/Rails编写了一段时间了,但由于这是我第一次尝试这种行为并且现有资源没有直接回答我的问题,我想从更多经验丰富的开发人员那里获得有关如何完成的反馈我的任务.

那么,如何从Rails中的超类调用子类的方法实现?有没有比我最终(几乎)实施更好的方式?

Dig*_*oss 22

好问题,但是你太复杂了.请记住一些原则,一切都应该清楚......

  • 这些类型将被动态解析,因此如果在实际调用它的时候对象的类层次结构show存在任何位置,那么Ruby将找到它并调用它.欢迎您输入对将来可能存在或不存在的任何内容的方法调用,它是合法的ruby语法,它将进行解析.您可以键入包含引用的表达式, 除非实际调用,否则没有人会关心.this_will_never_be_implemented

  • 即使在Java中,也只有一个实际对象.是的,您可能在超类中调用方法,但它派生类的实例(以及基类的实例),因此您可以依赖于show被调用的新实例.

  • 从某种意义上说,每个Ruby类都是一个抽象类,包含可能在将来定义的每种可能方法的存根.您可以在基类或派生类中调用没有访问限定符的任何内容.

如果您想要一个null超类实现,您可能希望定义一个不执行任何操作或引发异常的实现.

更新:可能,我应该只是说" show像任何其他方法一样调用"并将其留在那里,但是到目前为止我想补充一点:你也可以show使用Ruby的多重继承版本来实现:包括 SomeModule.由于您显然对Ruby的对象模型感兴趣,因此您可以使用mixin实现您的属性,只是为了好玩.

  • 我对这个答案的第一反应是"这与问题没有任何关系.你在解释面向对象." 但是在重新阅读这个问题时,我意识到OP绝对不知道面向对象是什么,所以这确实是唯一明智的答案.+1 (3认同)
  • @Shaun:这是C++中非虚函数的行为,它使用变量的静态类型(而不是绑定到变量的对象的类型)来解析方法调用.也许这就是你得到这个概念的地方. (3认同)
  • 谢谢,这清理了一切.在某个地方,我接受了一个概念,即在超类中引用self会得到一个超类类型的实例,并且实例无法响应子类功能.显然,这个概念是不正确的. (2认同)

小智 10

如你所知,让超类了解子类功能是一个很大的禁忌,这就是你想要抽象方法的原因.

你想要做的是show在你的超类中定义.然后你可以在超类中调用它,子类将调用它自己的版本,但是超类不会抛出错误.

class Media < ActiveRecord::Base
  def show
    # This method should be overloaded in a subclass
    puts "Called from Media" 
  end

  def do_something
    show
  end
end

class Image < Media
  def show
    puts "Called from Image" 
  end
end

class Video < Media
  def show
    puts "Called from Video" 
  end  
end

i = Image.new
i.do_something
=> Called from Image

v = Video.new
v.do_something
=> Called from Video
Run Code Online (Sandbox Code Playgroud)