Ruby 2.0 中模块前置的实现
作者:网络转载 发布时间:[ 2014/3/14 13:52:26 ] 推荐标签:Ruby 模块前置 工作
可以看到,它的行为和 Module#include 方法几乎一样,只不过回调的方法不一样。这里,它回调了参数模块的 prepend_features 方法和 prepended 方法。同样,Module#prepend_features 才是真正干活的地方,所以跟进去看看。
static VALUE
rb_mod_prepend_features(VALUE module, VALUE prepend)
{
switch (TYPE(prepend)) {
case T_CLASS:
case T_MODULE:
break;
default:
Check_Type(prepend, T_CLASS);
break;
}
rb_prepend_module(prepend, module);
return module;
}
它做了一些类型方面的检查,然后把工作交给了 rb_prepend_module 函数,我们看看 rb_prepend_module 函数做了什么。
void
rb_prepend_module(VALUE klass, VALUE module)
{
void rb_vm_check_redefinition_by_prepend(VALUE klass);
VALUE origin;
int changed = 0;
rb_frozen_class_p(klass);
if (!OBJ_UNTRUSTED(klass)) {
rb_secure(4);
}
Check_Type(module, T_MODULE);
OBJ_INFECT(klass, module);
origin = RCLASS_ORIGIN(klass);
if (origin == klass) {
origin = class_alloc(T_ICLASS, klass);
RCLASS_SUPER(origin) = RCLASS_SUPER(klass);
RCLASS_SUPER(klass) = origin;
RCLASS_ORIGIN(klass) = origin;
RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
RCLASS_M_TBL(klass) = st_init_numtable();
st_foreach(RCLASS_M_TBL(origin), move_refined_method,
(st_data_t) RCLASS_M_TBL(klass));
}
changed = include_modules_at(klass, klass, module);
if (changed < 0)
rb_raise(rb_eArgError, "cyclic prepend detected");
if (changed) {
rb_clear_cache();
rb_vm_check_redefinition_by_prepend(klass);
}
}
这个函数做了一些工作,我们来分析一下。前 16 行都是在做一些类型检查等工作,我们跳过。从第 17 行开始分析。
首先,宏 RCLASS_ORIGIN 获取 klass 的 origin 成员,并且把它和 klass 比较。我们不知道 origin 字段有什么作用,我们先假设测试条件为真,即 klass 的 origin 成员指向自身。我们来分析一下 if 语句中的逻辑:
19 行为 klass 创建了一个新的包含类,我们把它称为原始类;
20 ~ 21 行把新创建的包含类插入到 klass 和 klass 的父类中间;
22 行将 klass 的 origin 成员指向了新类;
接下来,23 ~ 24 行把 klass 的方法表转移到新类中,并清空 klass 的方法表;
后,25 行又把 klass 原先的方法表中的 Refined 方法移了回来。
分析完 if 语句,我们继续前进,来到第 28 行。等等,你好像看到了熟悉的东西。没错,那是 include_modules_at 方法。在前一篇文章中,我们讨论了这个函数,它用来包含某个模块。你简直不敢相信自己的眼睛,明明是在前置模块,怎么突然又变成包含模块了?
是的,没错,它是在包含模块。被包含的模块的祖先链插入到了 klass 和 klass 的原始类之间。由于 klass 内部的方法表已经转移到上游的原始类中,所以插入的位置正好合适。Ruby 通过这种变换,巧妙地将前置模块转化为包含模块,太棒了。
下面这个图描述了文章开头的那个例子中,类 C 中 prepend A, B 语句执行前后的状态:
+-----+ +--------+
Before: | C |----->| Object |
+-----+ +--------+
+--------------- klass ----------------+
| |
v |
+-----+ +-----+ +-----+ +-----+ +--------+
After: | C |----->| A |----->| B |----->| C' |+---->| Object |
+-----+ +-----+ +-----+ +-----+ +--------+
| ^
| |
+--------------- origin ---------------+
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11