【问题标题】:What is wrong with my to_s method for an inherited Circle class?对于继承的 Circle 类,我的 to_s 方法有什么问题?
【发布时间】:2012-11-16 01:14:32
【问题描述】:

我正在编写一个包含 4 个类(Point、Shape、Rectangle、Circle)的程序。 Rectangle 和 Circle 继承自 Shape,其中包含形状中心的信息(字段)。我正在尝试为 Circle 类编写一个 to_s 方法,它应该像下面这样打印出来:

圆:[(1, 2), 3]

(1,2) 是中心,3 是半径。这就是我的 to_s 方法:

def to_s
  "Circle: [(" + super.x.to_s + ", " + super.y.to_s + "), " + 
    radius.to_s + "]"
end

我收到一个错误“没有这样的方法 'x' 错误”,我知道这是因为 Shape 没有 'x' 方法(它在 Point 中)。我尝试了一些长方法链接,例如 super.center.y.to_s,但这有其自身的问题。这样做的正确方法是什么。即,良好的编程风格、面向对象的方式和 ruby​​ 方式?

【问题讨论】:

  • 我对你的类定义感到困惑。你能把它们包括在问题中吗?
  • 你应该使用字符串插值而不是加法,它更干净,更快。

标签: ruby oop inheritance


【解决方案1】:

调用super 发送父类的to_s 方法,然后您将发送xy 等的结果(字符串),这些方法对于字符串不存在。

试试这个:

def to_s
  "Circle: [(#{x},#{y}) #{radius}]"
end

Ruby 具有隐式字符串插值,这意味着在双引号字符串中,#{...} 中的任何内容都将自动呈现为字符串。

如果在ShapeCircle 中没有定义x,从x 得到什么? Shape 是否继承自 Point

编辑:

要访问保存xy 值的实例变量@center,您有几个选择。这是最基本的:

class Shape
  attr_accessor :center

  def initialize(point)
    @center = point
  end
end

class Circle

  def initialize(point, radius)
    super(point) # this runs Shape's initialize method
    @radius = radius
  end

  def to_s
    "Circle: [(#{center.x},#{center.y}) #{radius}]"
  end
end

另一种选择是使用内置的 Forwardable 模块:

require 'forwardable'
class Shape
  extend Forwardable
  def_delegators :@center, :x, :y
  #...
end

然后在您的实例中,您可以直接调用xy,它们将自动转发到@center,在这种情况下,“圆圈:[(#{x},#{y}) #{radius}]" 字符串会起作用。

【讨论】:

  • 我了解如何工作,但这并不能解决更大的问题,即超类没有'x'方法,所以它仍然会抛出该错误。
  • 错误是因为调用super路由到父类的to_s方法。即使在Shape 上定义了x,它也不起作用,因为将它链接到to_s 就像说"string".x。你的 x 和 y 来自哪里?
  • 点类。 Shape(Circle 的父级)有一个字段 @center,它是一个点,这就是我尝试使用 .x 进行方法链的原因。
  • 在这种情况下应该使用super 的唯一原因是参考to_s 方法的先前实现。正如 Zach 所说,直接调用您的 xy 方法。子类的原因是它们继承父类的方法。
【解决方案2】:

在 ruby​​ 中,super 只是一个表示重写方法的方法,而不是超类本身。

示例:

class A
    def to_s
        'A'
    end
end

p A.new.to_s #=> returns 'A'

class B < A
    def to_s
        super + 'with' + 'B'
    end
end

p B.new.to_s #=> returns 'A with B'

如果你不明白,调用super 只调用A.to_s 而不是A 本身,所以如果A 有一个属性x 调用super.x 会失败,因为super 只代表当前被覆盖的方法,而不是整个超类。现在,要访问超类的属性,您只需调用@x.to_sself.x.to_s,因为超类的属性属于子类。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多