我是一位经验丰富的Obj-C / Java程序员,我正在深入研究Ruby。显然它是如此动态的事实很棒(重新打开类很棒!)但是当我开始编写Ruby代码时,有一件事让我担心/担心。
我有兴趣知道你的Ruby-ers做了什么(如果有的话)在你自己的类中显式设置iVar的类型。从我所看到的,你可以将iVar设置为任何对象,ruby不会抱怨。但是如果你期望特定的iVar属于某种类型,那么它可能会导致问题。例如:
class MyString
def initialize(myString)
@myString = myString
end
def uppercase_my_string
@myString.upcase
end
end
st1 = MyString.new("a string!")
st1.uppercase_my_string
st2 = MyString.new(["a string"])
st2.uppercase_my_string
此代码将抛出NoMethodError
,因为数组当然没有方法upcase
。不幸的是,它没有告诉我们哪里出错了(上面的行,创建str2
时),所以我们在调试时没有多大帮助(如果str2
碰巧在某些模块中创建了几个模块不起眼的地方)
一个自然的步骤可能是添加一些检查initialize
,如下所示:
class MyString
def initialize(myString)
raise TypeError, "myString iVar is not a string!" unless myString.class == String
@myString = myString
end
end
...same code as before
太好了,现在如果我们不小心创建了一个新的MyString,我们就会被告知我们有多傻(更重要的是,当我们这样做时我们会被告知,而不是当我们失败时。有点痛苦,但是没关系。
我的下一个问题是当我们决定在iVar上使用attr_accessors
时。
class MyString
attr_accessor :my_string
def initialize(my_string)
raise TypeError, "myString iVar is not a string!" unless my_string.class == String
@my_string = my_string
end
def uppercase_my_string
@my_string.upcase
end
end
st1 = MyString.new("a string!")
st1.uppercase_my_string
st2 = MyString.new("good, it's a string")
st2.my_string = ["an array!"]
st2.uppercase_my_string
使用定义的setter,我们可以非常偷偷摸摸地绕过initialize
中的错误检查。这又有一个问题,就是在uppercase_my_string
中抛出异常,而不是在我们意外地将@my_string
设置为数组时。
最后,我们可以手动创建访问器并添加错误检查,但这是一个巨大的痛苦......有更快更容易的方法来做到这一点。 或者我只是想要关闭而不够动态?
谢谢!
除此之外:我知道在Obj-C中你在运行时仍然遇到同样的问题,但通常你会发现编译器错误,说你正在将array
类型的对象分配给类型为{的变量{1}}(或类似的东西),所以至少我们会被警告它发生的地方
答案 0 :(得分:4)
在实践中,这些类型的问题实际上非常罕见。如果你想成为偏执狂(因为他们你真的是为了得到你),你可以通过to_s
发送输入,以确保你总是有一个字符串:
def initialize(my_string)
@my_string = my_string.to_s
end
然后你可以说MyString.new(6)
,一切都会按预期工作。当然,你可以说MyString.new([6, 11])
并且胡说八道。
如果您真的希望my_string
成为字符串,则不会明确检查其class
。如果某人有子类字符串,那么这将导致问题,因此您至少要使用is_a?
:
def initialize(myString)
raise TypeError, ... unless myString.is_a? String
@myString = myString
end
您还可以检查to_str
方法:
def initialize(myString)
raise TypeError, ... unless myString.respond_to? :to_str
@myString = myString.to_str
end
实现该方法会(在某些圈子中)表明你的东西是String类似于String。我认为调用to_s
会是一个更好的主意,这会使事情表现得像人们在Ruby中所期望的那样。
就你的mutator问题而言:
st2.my_string = ["an array!"]
您不必让任何人在属性中写入任何内容:类不是结构。您只能自动定义访问者并编写自己的mutator以自动在to_s
调用中滑动:
class MyString
attr_reader :my_string
def initialize(my_string)
self.my_string = my_string
end
def my_string=(s)
@my_string = s.to_s
end
def uppercase_my_string
@my_string.upcase
end
end
基本上,你不必担心Ruby中的类型,你担心什么方法会响应。而且,如果你想要一些特别的字符串,你可以通过调用通用的to_s
方法(这是字符串插值,"#{x}"
)来使它成为一个字符串。
答案 1 :(得分:2)
我认为如果你的程序将错误类型的对象传递给一个类,那么这实际上不是一个应该被实例化或变异的对象解决的问题。即使有额外的类型检查和错误抛出,你的程序仍然将错误的对象传递给你的类,并没有真正使你的程序不那么错误。
在使用MyString
类的程序中,应该有一个contract,例如协议,给予构造函数的对象实现upcase
方法。
测试和文档也被用来禁止程序中的这些缺陷。但我认为这可能是一个单独的讨论。