我需要将一个类的示例写入一个变量,并且能够访问它来获取它的值和访问它的方法。我能以某种方式实现这一点吗?
举例来说:
class A
def initialize(value)
@value = value
end
def preview
puts "Class preview: #{@value}"
end
def something(param)
puts "Something method: #{@value * param}"
end
end
class B
attr_reader :obj
def set_object(obj)
@obj = obj
end
end
b = B.new
b.set_object(A.new(5))
b.obj # ==> 5
10 + b.obj # ==> 15
b.obj.preview # ==> "Class preview: 5"
b.obj.something(3) # ==> "Something method: 15"
字符串
1条答案
按热度按时间xcitsw881#
代码的基本问题是,您正在实现一个行为类似于数字的对象,但没有遵循Ruby的类似数字类型的协议,如
Numeric
类所述。特别是,您缺少
Numeric#coerce
方法提供的arithmetic coercion protocol。在Ruby中,每当算术运算符不知道如何处理其操作数时,它将向操作数发送
coerce
消息,并告诉它使用它知道如何处理的一对操作数进行响应。例如,当将
+
消息发送到10
并将A
的示例作为参数传递时,将调用方法Integer#+
。因此,在这一行中:字符串
在这里,您将消息
+
发送到Integer
文本10
(计算结果),并将表达式b.obj
(即A
的示例)的计算结果作为参数传递。所以,我们在这里基本上是:
型
当然,现在的问题是,
Integer#+
不知道如何将A
的示例添加到自己。然而,每个算术操作都遵守 * 算术强制 * 协议,即Integer#+
的实现看起来有点像这样:型
你看到这把戏了吗?
Integer#+
不知道如何添加Integer
s和A
s,这并不奇怪,因为你今天才写了A
,而Integer#+
几乎是30年前写的。* 然而 *,由于Integer
是一个标准的Ruby类,它假设A
知道如何处理Integer
s!所以,
Integer#+
在这里所做的是,它调用A
的coerce
方法,并说“嘿,我不知道你是什么,但在这里我把自己作为参数传递,我希望你知道我是什么,所以请把我和你自己转换成知道如何把两者相加的东西”。这意味着,我们需要为
A
实现一个coerce
方法。coerce
的协议如下:coerce
被发送到算术运算的 * 右操作数 *,带有一个参数,这是算术运算的 * 左 * 操作数。coerce
需要返回一对对象[coerced_left_operand, coerced_right_operand]
。coerce
需要确保这个过程最终终止,也就是说,两个强制返回值中的 * 至少有一个 * 应该 * 更接近于内建类型 *。所以,让我们实现
coerce
:型
现在,如果我们运行问题中的代码,我们得到:
型
因此,正如您所看到的,表达式
10 + b.obj
被正确地计算为15
。代码的下一个问题是,你没有覆盖一些应该被对象覆盖的标准方法,我说的是像
BasicObject#==
,Object#eql?
,Object#hash
,Object#to_s
等方法。特别是,为了显示一个对象的人类可读的调试表示而发送的消息是
inspect
。我们没有覆盖Object#inspect
,所以我们得到了默认的实现,它包含关于类的信息,一个实现定义的标识符,以及一个示例变量及其值的列表。我们需要重写
inspect
来更像这样工作:型
现在,运行问题中的代码,产生所需的结果:
型
在你的代码中还有一些其他的东西可以改进。
A
旨在与数字类似,因此它应该继承自Numeric
。A
是类整数,它应该响应to_int
。to_int
的东西,在逻辑上也应该响应to_i
。A
是类似数字的,它应该实现算术运算。to_s
以提供更合理的字符串表示。==
以提供更合理的相等语义。eql?
和hash
,以提供更合理的集合成员资格和哈希语义。set_
为前缀,而应该命名为foo=
。Module#attr_reader
、Module#attr_writer
或Module#attr_accessor
来定义它们。如果我们把这些放在一起,我们会得到这样的东西:
型