Monday, October 20, 2008

(24)“冻结”和“污染”的对象

出于某些需求你可能不希望你的对象状态被修改,那么在Ruby中你可以通过调用freeze方法实现,如果一个对象被freeze那么任何试图修改对象状态的行为都会抛出异常,这是对象变成了一个不可变对象:
s = "ice"      # 字符串是一个可变对象
s.freeze # 标记这个对象为不可变
s.frozen? # true: 用frozen?方法检查这个对象是否被冻结了
s.upcase! # TypeError: 不能修改一个冻结的对象
s[0] = "ni" # TypeError: 不能修改一个冻结的对象
说到这里我们再来说说对象的复制。Ruby定义了clone和dup方法来复制对象,当你执行这个两个方法的时候你会得到这个对象的拷贝,如果这个对象中存在对另一个对象的内部引用,你只能复制另一个对象的引用而不是它本身。如果你的对象中定义了initialize_copy方法,那么在调用clone和dup方法的时候会触发这个initialize_copy方法来初始化你刚刚复制的对象拷贝。那么这里clone和dup方法对于已经冻结的对象,复制拷贝的结果就不同了,clone得到的是一个冻结的对象,而dup方法得到的是一个“解冻”的对象。
我们的应用程序经常要面对很对安全问题,比如未信任的用户SQL注入操作。Ruby提供了一个方便的方案来处理这一问题。我们可以通过tainted方法来标明一个对象被“污染了”。一旦一个对象被污染了,那么所有派生自他的对象都是被污染的对象,你可以通过tainted?方法检测一个对象是否被污染了:
s = "untrusted"   # 缺省情况下,一个对象是没有被污染的。
s.taint # 把这个对象标记为被污染了
s.tainted? # true: 检查对象是否被污染了
s.upcase.tainted? # true: 被污染对象的派生对象也是被污染的
s[3,4].tainted? # true
通过gets方法读入的用户输入内容:比如命令行参数,环境变量,字符串都自动被标记为感染的。通过clone和dup复制的对象都是“污染的”对象,你可以通过untainted方法标记一个对象没有被污染。
当你设置了全局变量$SAFE时,这种机制非常有用,如果你设置$SAFE为任何非0的值,那么Ruby会限制所有的方法不能使用被污染的对象。

No comments: