Ruby中的多重继承类型类继承

6
我有一个名为Base的超类和一堆派生类,例如Base::NumberBase::Color等。我想能够像使用Fixnum那样使用这些子类,比如在Number的情况下。

最好的方法是什么?同时仍然要让它们适当地响应is_a? Base吗?

因此,我应该能够执行以下操作:

Number.new(5) + Number.new(6) # => 11
Number.new.is_a? Base         # => true

我在考虑是否可以混入Base,并覆盖is_a?,kind_of?和instance_of?方法,但希望有更简洁的方法。

4个回答

13

使用Ruby实现这个其实很简单:

module Slugish
  attr_accessor :slug
  def loud_slug
    "#{slug}!"
  end
end

class Stringy < String
  include Slugish
end

class Hashy < Hash
  include Slugish
end

hello = Stringy.new("Hello")
world = Stringy.new("World")

hello.slug = "So slow"
world.slug = "Worldly"

hello.loud_slug      #=> "So slow!"
world.loud_slug      #=> "Worldly!"

hello.is_a?(Slugish) #=> true
world.is_a?(Slugish) #=> true

"#{hello} #{world}"  #=> "Hello World"

stuff = Hashy.new
stuff[:hello] = :world
stuff.slug = "My stuff"
stuff.loud_stug      #=> "My stuff!"
stuff.is_a?(Slugish) #=> true

1
哦,哇,我不知道is_a?对于mixins的响应是“true”,而且即使包含的模块包含另一个模块,它也可以工作!我可能会使用这个,而不是forwardable。谢谢! - cloudhead
1
笔误:应该是 world = Stringy.new("World") - rampion

3
为什么你坚持使用is_a?/kind_of?,而不是respond_to?这种更简洁的检查方式呢?你希望对象实现一个接口/协议,而不是成为任意选择的超类的子类。但也许我在这里缺少某种要求。

编辑:我理解你的推理,但它往往会导致较差的面向对象/动态设计。或者你正在做这样的事情,在叶子类中这可能是可以接受的想法,但在框架中应该通过继承来解决:

if a.is_a?(something)
   #do something
elif a.is_a?(something_else)
   #do something else
...

或者是像这样的内容:
if !a.is_a?(something)
   #raise condition/return null/etc.
endif 
...

我认为,在基于消息传递的语言中,让代码失败并抛出“不理解”的异常是一个完美的设计决策。
另外,使用"is_a?"而不是"respond_to?"会限制您在单元测试时使用模拟对象的能力,这可能会对相当复杂的代码造成很大问题。

2
respond_to?并不总是更简洁,因为它响应一个方法不意味着之后你发送的所有其他调用它都会响应。所以在这种情况下,只检查一次类型是否正确会更好。 - cloudhead

1

我认为如果完全没有关系的类,如“Number”和“Color”都派生自同一基类,则您正在错误地使用继承。 如果它们确实需要访问相同的例程(不确定为什么会这样),那么我会使用组合代替。


1
数字和颜色确实没有关联。继承是一种“是一个”关系,而不是“有一个”。使用组合。 - Ed S.
而String和Fixnum是没有关系的,但它们都继承自Object。真奇怪! - cloudhead
一切都继承自Object,因为一切都是对象。显然你不理解何时使用继承和何时使用组合,所以我建议找一个面向对象编程的入门教程。如果你不那么无知地寻求另一个意见,我可以告诉你它们之间的区别。 - Ed S.
但是这一点信息非常重要。对我而言(以及其他人),看起来你正在使用继承,而组合更适合这个任务。 - Ed S.
1
@ed,我建议你冷静下来回答这个问题。他可能正在做一些违反你的OO原则(也许是我的),但我们没有足够的信息来确定这一点,所以我建议你只是展示Ruby在这方面的强大之处,然后就此作罢。 - Yehuda Katz
显示剩余6条评论

1
Ruby的多重继承等价物是mixin。听起来你想要的是将Base作为一个模块混入到几个类中。

是的,我不知道包含的模块在is_a方面会像超类一样行事?这基本上使它成为一个无关紧要的问题。 - cloudhead

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接