【发布时间】:2011-10-21 20:12:22
【问题描述】:
我想创建一个充当特定类的对象,例如 Fixnum,但它既不是该类的实例,也不是它的子类。
这有多种用例。在 Fixnum 的情况下,我想定义一个更具体的整数类型,它本质上是一个 Fixnum,但也实现了一些额外的逻辑。我不能对 Fixnum 本身进行子类化,因为不能对 Fixnum 和 Symbol 等直接类型进行子类化。
另一个用例是自动化测试中的模拟:有时您想创建一个对象,该对象的行为类似于某个类(通常是模型实例),但由于技术原因不是该确切类的实例。
以下是如何创建将所有方法委托给内部存储的固定编号的特定整数类型:
require 'delegate'
require 'forwardable'
# integer representing a page number
class PageNumber < DelegateClass(Integer)
extend Forwardable
def initialize(value, name)
@name = name
super(value)
end
def inspect
"#{@name} #{to_i}"
end
alias_method :to_i, :__getobj__
def_delegators :to_i, :instance_of?, :kind_of?, :is_a?
end
这个对象可以通过is_a?和类似的检查:
page = PageNumber.new(1, "page")
page.is_a? Fixnum #=> true
但我所做的一切都无法让它通过Module#=== 类型检查:
# my problem:
Fixnum === page #=> false
不幸的是,我的对象未能通过此检查,因为=== 方法在case 语句内部使用:
case page
when Fixnum
# it will never get here
when String
# ...
else
# ...
end
我的问题是如何创建一个通过=== 检查的模拟类型而不增加内置类上的=== 方法?
【问题讨论】:
-
注意:我在这里询问 MRI。不同的 Ruby 实现对
===的实现方式不同,因此一个解决方案可能不适用于其他解决方案。 -
“我不能继承 Fixnum 本身,因为像 Fixnum 和 Symbol 这样的直接类型不能被继承。”这是为什么?我只是尝试将 Fixnum 子类化,它在 1.8.7 和 1.9.2 中工作
-
@jigfox,现在尝试做
Mynum.new并看着它失败。直接类型在内部以一种阻止它们的子类可用的方式表示;以these checks 为例。