学无所长,一事无成
分类: Python/Ruby
2012-07-13 18:19:45
2.2 动态方法 p40
通过 send 向接收者发送消息,如:
- obj.my_method(3) #等价于
- obj.send(:my_method,3)
send 的第一个参数可以是字符串,也可以是符号。剩余参数(以及代码块)会直接传递给调用的方法。
符号:符号是不可变的。
字符串转换成符号: String#to_sym() 或者 String#intern()
符号转换成字符串: Symbol#to_s() 或者 Symbol#id2name()
来自 Camping 的例子
读取配置文件,调用方法
配置文件: admin : bill
转换后调用: conf.admin = ‘bill’
代码实现:
动态定义方法
可以利用 Module#define_method() 方法定义一个方法,如:
注: define_method 需要调用,才能动态创建方法。因此需要一定的动作才能触发。在 class 定义时是一个时机,因为在 class …. end 间是可以运行代码的(见上例)。另外我们可以放在 initialize 中,在初始化对象时,自动生成,这是一个比较恰当的时机。另外也可以放在其他的一些方法中。根据实际需要来选择了。
思考:我们知道对象的方法是放在类中的,那么动态方法是放在那里的呢?显然不会是类中,因为动态生成的方法需要根据不同的实例而变化。
2.3 method_missing() 方法
当一个对象就受到一个方法调用时,会到该对象的类中查询它的实例方法。找不到,则沿着祖先链向上搜索进入 Object ,并最终来到 Kernel 模块。最终还没找到,则在最初的接受者上调用 method_missing() 方法,这是Kernel 的一个实例方法,每个对象都会继承。尽管这是一个私有方法,我们还是可以通过 send() 来调用:
覆写 method_missing() 方法
可以使你调用实际上并不存在的方法。
幽灵方法
被 method_missing() 方法处理的消息,从调用者角度看,跟普通方法没有什么区别,但是实际上接受者并没有相对应的方法。这被称为一个有幽灵方法。
动态代理
通过封装对象的 method_missing() 方法来收集方法调用,并将其转发到被封装的对象上。
代理:你可以使用 Ruby 的 delegate 库来快速得到一个使用的动态代理。
覆写 respond_to?() 方法
当调用一个幽灵方法是,通过 Object#methods() 是查不到的。通过 repond_to?() 查找该方法时,也会返回 false 。因此我们覆写 method_missing() 方法的同时,也需要覆写 respond_to?() 方法。
当引用一个不存在的常量时,Ruby 将把这个常量名作为一个符号传递给 const_missing() 方法
2.4 小测试:狩猎 Bug
当输入错误,或者作用域超出的时候。method_missing()会做出一些默认处理动作,或许这不是你想要的,而且这种错误由于覆写了 method_missing 很难发现。因此我们需要尽可能的限定 method_missing 的动作。(把无限责任变成有限责任,也可以说是白名单跟黑名单的区别,或者说是无罪推定跟有罪推定原则的区别:我默认 method_missing 是会犯错的,因此需要自证无罪,既约束代码,限定责任;)
2.5 关于 method_missing() 方法的更多内容
当方法冲突时
当一个幽灵方法和一个真实的方法发生名字冲突时,真实方法会胜出。为了安全起见,你应该在代理类中删除绝大多数继承来的方法。这就是所谓的白板类,他拥有的方法比 Object 类还要少(因此 Ruby 1.9.3 引入了 BasicObject)。
哈,这又是一个有罪推定。我个人总结:在设计对象模型时,我们采用无罪推定,充分信任每一个类或模块。而在具体设计某个类或模块时,要采用有罪推定,坚决不相信外部传入的任何东西,严格进行规制校验。如此才能创建良好封装,灵活可扩展,以及安全稳固的对象体系。
可以使用 Module#undef_method()方法来删除所有的(包括继承来的)方法;也可以使用 Module#remove_method() 方法,它只会删除接受者自己的方法。
性能方面的忧虑
总体来说,使用幽灵方法比普通方法要慢,因为需要更长的查找路径。但或许现有软件体系最慢的部分不是代码,而是IO,这个需要你权衡了。另外也有一些折中的方法,如第一次调用幽灵方法是,为它创建一个动态方法。
保留方法
Ruby 中目前有两个保留方法: __send__() 和 __id__() 。
BasicObject
我个人的偏好,除非万不得已,不要使用幽灵方法。六岁孩童使用千斤巨锤,非但不能发挥威力,反易自残。当然,牛人除外(牛人的特点就是喜欢剑走偏锋)。